[mirotalksfu] - #194 Improvements

هذا الالتزام موجود في:
Miroslav Pejic
2025-02-15 23:01:06 +01:00
الأصل c719fdc0c9
التزام c999eff718
5 ملفات معدلة مع 50 إضافات و18 حذوفات

عرض الملف

@@ -58,7 +58,7 @@ dev dependencies: {
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.42 * @version 1.7.43
* *
*/ */

عرض الملف

@@ -1,6 +1,6 @@
{ {
"name": "mirotalksfu", "name": "mirotalksfu",
"version": "1.7.42", "version": "1.7.43",
"description": "WebRTC SFU browser-based video calls", "description": "WebRTC SFU browser-based video calls",
"main": "Server.js", "main": "Server.js",
"scripts": { "scripts": {

عرض الملف

@@ -64,7 +64,7 @@ let BRAND = {
}, },
about: { about: {
imageUrl: '../images/mirotalk-logo.gif', imageUrl: '../images/mirotalk-logo.gif',
title: '<strong>WebRTC SFU v1.7.42</strong>', title: '<strong>WebRTC SFU v1.7.43</strong>',
html: ` html: `
<button <button
id="support-button" id="support-button"

عرض الملف

@@ -9,7 +9,7 @@
* @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.7.42 * @version 1.7.43
* *
*/ */

عرض الملف

@@ -2,14 +2,27 @@
class WebRTCStreamProcessor { class WebRTCStreamProcessor {
constructor() { constructor() {
this.segmentation = new SelfieSegmentation({ this.segmentation = null;
locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`, this.initialized = false;
});
this.segmentation.setOptions({ modelSelection: 1 }); // 1 = High accuracy
} }
async initializeSegmentation() { async initializeSegmentation() {
await this.segmentation.initialize(); // Ensure MediaPipe is ready if (!this.segmentation) {
this.segmentation = new SelfieSegmentation({
locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`,
});
this.segmentation.setOptions({ modelSelection: 1 });
}
if (!this.initialized) {
try {
await this.segmentation.initialize();
this.initialized = true;
console.log('✅ Segmentation initialized successfully.');
} catch (error) {
console.error('❌ Error initializing segmentation:', error);
}
}
} }
async processStreamWithSegmentation(videoTrack, maskHandler) { async processStreamWithSegmentation(videoTrack, maskHandler) {
@@ -18,6 +31,11 @@ class WebRTCStreamProcessor {
await this.initializeSegmentation(); // Ensure segmentation is initialized await this.initializeSegmentation(); // Ensure segmentation is initialized
if (!this.segmentation) {
console.error('❌ Segmentation is still undefined after initialization!');
return new MediaStream([videoTrack]); // Fallback to original video stream
}
const processSegmentation = async (videoFrame, controller) => { const processSegmentation = async (videoFrame, controller) => {
try { try {
const imageBitmap = await createImageBitmap(videoFrame); const imageBitmap = await createImageBitmap(videoFrame);
@@ -25,7 +43,9 @@ class WebRTCStreamProcessor {
// Process segmentation // Process segmentation
this.segmentation.onResults(async (results) => { this.segmentation.onResults(async (results) => {
if (!results || !results.segmentationMask) { if (!results || !results.segmentationMask) {
console.warn('Mediapipe segmentationMask is missing, skipping frame.'); console.warn('⚠️ Segmentation mask is missing, skipping frame.');
videoFrame.close();
imageBitmap.close();
return; return;
} }
@@ -43,14 +63,19 @@ class WebRTCStreamProcessor {
const newFrame = new VideoFrame(canvas, { timestamp: videoFrame.timestamp }); const newFrame = new VideoFrame(canvas, { timestamp: videoFrame.timestamp });
controller.enqueue(newFrame); controller.enqueue(newFrame);
// Close VideoFrame after processing // Close VideoFrame & imageBitmap after processing
videoFrame.close(); videoFrame.close();
imageBitmap.close();
}); });
// Send frame to Mediapipe // Send frame to MediaPipe
await this.segmentation.send({ image: imageBitmap }); await this.segmentation.send({ image: imageBitmap }).catch((err) => {
console.error('❌ Segmentation processing failed:', err);
return null;
});
} catch (error) { } catch (error) {
console.error('Error in processSegmentation:', error); console.error('Error in processSegmentation:', error);
videoFrame.close();
} }
}; };
@@ -98,10 +123,7 @@ class WebRTCStreamProcessor {
} }
async applyVirtualBackgroundToWebRTCStream(videoTrack, image = 'https://i.postimg.cc/t9PJw5P7/forest.jpg') { async applyVirtualBackgroundToWebRTCStream(videoTrack, image = 'https://i.postimg.cc/t9PJw5P7/forest.jpg') {
const backgroundImage = new Image(); const backgroundImage = await this.loadImage(image);
backgroundImage.crossOrigin = 'anonymous'; // Ensure the image is not tainted
backgroundImage.src = image;
await backgroundImage.decode(); // Ensure the image is loaded
const maskHandler = async (ctx, canvas, mask, imageBitmap) => { const maskHandler = async (ctx, canvas, mask, imageBitmap) => {
// Apply the mask to keep the person in focus // Apply the mask to keep the person in focus
@@ -123,4 +145,14 @@ class WebRTCStreamProcessor {
return this.processStreamWithSegmentation(videoTrack, maskHandler); return this.processStreamWithSegmentation(videoTrack, maskHandler);
} }
async loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = src;
img.onload = () => resolve(img);
img.onerror = reject;
});
}
} }