// Elements const videoElement = document.getElementById("live-preview"); const canvas = document.getElementById("output-canvas"); const ctx = canvas.getContext("2d"); const videoSourceSelect = document.getElementById("video-source"); const videoStatus = document.getElementById("video-status"); const enableBgRemoval = document.getElementById("enable-bg-removal"); const showPreview = document.getElementById("show-preview"); const modelQuality = document.getElementById("model-quality"); const edgeSmoothness = document.getElementById("edge-smoothness"); const backgroundBlur = document.getElementById("background-blur"); const foregroundBrightness = document.getElementById("foreground-brightness"); const processingTimeDisplay = document.getElementById("processing-time"); const fpsDisplay = document.getElementById("fps"); const copyUrlButton = document.getElementById("copy-url"); const customImageInput = document.getElementById("custom-image-input"); const customVideoInput = document.getElementById("custom-video-input"); // Canvas setup canvas.width = 1920; canvas.height = 1080; // Variables let customImage = null; let customVideo = null; let lastFrameTime = performance.now(); let currentBackground = "transparent"; // Populate video sources async function populateVideoSources() { try { const devices = await navigator.mediaDevices.enumerateDevices(); videoSourceSelect.innerHTML = ''; devices .filter((device) => device.kind === "videoinput") .forEach((device) => { const option = document.createElement("option"); option.value = device.deviceId; option.text = device.label || `Camera ${videoSourceSelect.options.length}`; videoSourceSelect.appendChild(option); }); } catch (err) { console.error("Lỗi khi liệt kê thiết bị video:", err); videoStatus.textContent = "Không thể liệt kê thiết bị video."; } } // Start video stream async function startVideo(deviceId) { try { const stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: deviceId ? { exact: deviceId } : undefined }, }); videoElement.srcObject = stream; videoElement.play(); videoStatus.textContent = ""; processFrame(); } catch (err) { console.error("Lỗi khi truy cập webcam:", err); videoStatus.textContent = "Không thể truy cập webcam. Vui lòng kiểm tra quyền camera."; } } // Simulated background removal (replace with real AI model in production) function simulateBackgroundRemoval(imageData) { const data = imageData.data; for (let i = 0; i < data.length; i += 4) { // Simple green-screen-like removal (replace with AI model output) const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // Example: Remove pixels with high green value if (g > 150 && r < 100 && b < 100) { data[i + 3] = 0; // Set alpha to 0 (transparent) } } return imageData; } // Process video frame function processFrame() { const startTime = performance.now(); // Draw video to canvas ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height); // Apply background removal if enabled if (enableBgRemoval.checked) { let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); imageData = simulateBackgroundRemoval(imageData); // Replace with AI model ctx.putImageData(imageData, 0, 0); // Apply background const tempCanvas = document.createElement("canvas"); tempCanvas.width = canvas.width; tempCanvas.height = canvas.height; const tempCtx = tempCanvas.getContext("2d"); // Draw background if (currentBackground === "solid-black") { tempCtx.fillStyle = "black"; tempCtx.fillRect(0, 0, canvas.width, canvas.height); } else if (currentBackground === "solid-gray") { tempCtx.fillStyle = "gray"; tempCtx.fillRect(0, 0, canvas.width, canvas.height); } else if (currentBackground === "solid-green") { tempCtx.fillStyle = "green"; tempCtx.fillRect(0, 0, canvas.width, canvas.height); } else if (currentBackground === "custom-image" && customImage) { tempCtx.drawImage(customImage, 0, 0, canvas.width, canvas.height); } else if (currentBackground === "custom-video" && customVideo) { tempCtx.drawImage(customVideo, 0, 0, canvas.width, canvas.height); } else if (currentBackground === "blurred") { tempCtx.filter = `blur(${backgroundBlur.value}px)`; tempCtx.drawImage(videoElement, 0, 0, canvas.width, canvas.height); } // Draw foreground tempCtx.globalCompositeOperation = "destination-over"; tempCtx.drawImage(canvas, 0, 0); // Apply brightness tempCtx.filter = `brightness(${foregroundBrightness.value})`; tempCtx.drawImage(tempCanvas, 0, 0); // Copy to main canvas ctx.filter = `blur(${edgeSmoothness.value}px)`; // Edge smoothness ctx.drawImage(tempCanvas, 0, 0); } // Update performance metrics const endTime = performance.now(); const processingTime = endTime - startTime; processingTimeDisplay.textContent = `${processingTime.toFixed(2)} ms`; fpsDisplay.textContent = `${(1000 / processingTime).toFixed(2)}`; lastFrameTime = endTime; // Continue processing requestAnimationFrame(processFrame); } // Event listeners videoSourceSelect.addEventListener("change", () => { startVideo(videoSourceSelect.value); }); showPreview.addEventListener("change", () => { videoElement.style.display = showPreview.checked ? "block" : "none"; }); document.querySelectorAll('input[name="background"]').forEach((input) => { input.addEventListener("change", () => { currentBackground = input.value; }); }); customImageInput.addEventListener("change", (e) => { const file = e.target.files[0]; if (file) { customImage = new Image(); customImage.src = URL.createObjectURL(file); currentBackground = "custom-image"; document.querySelector('input[value="custom-image"]').checked = true; } }); customVideoInput.addEventListener("change", (e) => { const file = e.target.files[0]; if (file) { customVideo = document.createElement("video"); customVideo.src = URL.createObjectURL(file); customVideo.loop = true; customVideo.play(); currentBackground = "custom-video"; document.querySelector('input[value="custom-video"]').checked = true; } }); copyUrlButton.addEventListener("click", () => { const url = window.location.href; navigator.clipboard.writeText(url).then(() => { alert("Đã sao chép URL nguồn trình duyệt OBS!"); }); }); // Initialize populateVideoSources();