File size: 7,086 Bytes
658eafa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// 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 = '<option value="">Select a video source</option>';
        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();