[mirotalksfu] - add video mirror
هذا الالتزام موجود في:
@@ -55,7 +55,7 @@ dev dependencies: {
|
||||
* @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
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.74
|
||||
* @version 1.5.75
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -385,6 +385,7 @@ module.exports = {
|
||||
},
|
||||
producerVideo: {
|
||||
videoPictureInPicture: true,
|
||||
videoMirrorButton: true,
|
||||
fullScreenButton: true,
|
||||
snapShotButton: true,
|
||||
muteAudioButton: true,
|
||||
@@ -392,6 +393,7 @@ module.exports = {
|
||||
},
|
||||
consumerVideo: {
|
||||
videoPictureInPicture: true,
|
||||
videoMirrorButton: true,
|
||||
fullScreenButton: true,
|
||||
snapShotButton: true,
|
||||
focusVideoButton: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mirotalksfu",
|
||||
"version": "1.5.74",
|
||||
"version": "1.5.75",
|
||||
"description": "WebRTC SFU browser-based video calls",
|
||||
"main": "Server.js",
|
||||
"scripts": {
|
||||
@@ -64,7 +64,7 @@
|
||||
"compression": "1.7.4",
|
||||
"cors": "2.8.5",
|
||||
"crypto-js": "4.2.0",
|
||||
"dompurify": "^3.1.6",
|
||||
"dompurify": "^3.1.7",
|
||||
"express": "4.21.0",
|
||||
"express-openid-connect": "^2.17.1",
|
||||
"fluent-ffmpeg": "^2.1.3",
|
||||
@@ -77,7 +77,7 @@
|
||||
"mediasoup-client": "3.7.16",
|
||||
"ngrok": "^5.0.0-beta.2",
|
||||
"nodemailer": "^6.9.15",
|
||||
"openai": "^4.63.0",
|
||||
"openai": "^4.65.0",
|
||||
"qs": "6.13.0",
|
||||
"socket.io": "4.8.0",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
|
||||
@@ -442,10 +442,6 @@ th {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
#VideoMirrorDiv {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
# RTMP settings
|
||||
--------------------------------------------------------------*/
|
||||
|
||||
@@ -192,6 +192,7 @@ video {
|
||||
object-fit: var(--videoObjFit);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
#canvasAIElement {
|
||||
|
||||
@@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h
|
||||
* @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
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.74
|
||||
* @version 1.5.75
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -213,6 +213,7 @@ let isEnumerateVideoDevices = false;
|
||||
let isAudioAllowed = false;
|
||||
let isVideoAllowed = false;
|
||||
let isVideoPrivacyActive = false;
|
||||
let isInitVideoMirror = false;
|
||||
let isRecording = false;
|
||||
let isAudioVideoAllowed = false;
|
||||
let isParticipantsListOpen = false;
|
||||
@@ -658,6 +659,10 @@ function setupInitButtons() {
|
||||
initStopScreenButton.onclick = async () => {
|
||||
await toggleScreenSharing();
|
||||
};
|
||||
initVideoMirrorButton.onclick = () => {
|
||||
initVideo.classList.toggle('mirror');
|
||||
isInitVideoMirror = initVideo.classList.contains('mirror');
|
||||
};
|
||||
}
|
||||
|
||||
// ####################################################
|
||||
@@ -925,7 +930,11 @@ async function whoAreYou() {
|
||||
});
|
||||
const serverButtons = response.data.message;
|
||||
if (serverButtons) {
|
||||
BUTTONS = serverButtons;
|
||||
// Merge serverButtons into BUTTONS, keeping the existing keys in BUTTONS if they are not present in serverButtons
|
||||
BUTTONS = {
|
||||
...BUTTONS, // Spread current BUTTONS first to keep existing keys
|
||||
...serverButtons, // Overwrite or add new keys from serverButtons
|
||||
};
|
||||
console.log('04 ----> AXIOS ROOM BUTTONS SETTINGS', {
|
||||
serverButtons: serverButtons,
|
||||
clientButtons: BUTTONS,
|
||||
@@ -1980,6 +1989,7 @@ function setButtonsInit() {
|
||||
setTippy('initAudioVideoButton', 'Toggle the audio & video', 'top');
|
||||
setTippy('initStartScreenButton', 'Toggle screen sharing', 'top');
|
||||
setTippy('initStopScreenButton', 'Toggle screen sharing', 'top');
|
||||
setTippy('initVideoMirrorButton', 'Toggle video mirror', 'top');
|
||||
}
|
||||
if (!isAudioAllowed) hide(initAudioButton);
|
||||
if (!isVideoAllowed) hide(initVideoButton);
|
||||
@@ -2246,11 +2256,13 @@ function handleCameraMirror(video) {
|
||||
// Desktop devices...
|
||||
if (!video.classList.contains('mirror')) {
|
||||
video.classList.toggle('mirror');
|
||||
isInitVideoMirror = true;
|
||||
}
|
||||
} else {
|
||||
// Mobile, Tablet, IPad devices...
|
||||
if (video.classList.contains('mirror')) {
|
||||
video.classList.remove('mirror');
|
||||
isInitVideoMirror = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2347,11 +2359,6 @@ function handleSelects() {
|
||||
lS.setSettings(localStorageSettings);
|
||||
e.target.blur();
|
||||
};
|
||||
switchVideoMirror.onchange = (e) => {
|
||||
rc.toggleVideoMirror();
|
||||
rc.roomMessage('toggleVideoMirror', e.currentTarget.checked);
|
||||
e.target.blur();
|
||||
};
|
||||
switchSounds.onchange = (e) => {
|
||||
isSoundEnabled = e.currentTarget.checked;
|
||||
rc.roomMessage('sounds', isSoundEnabled);
|
||||
@@ -2903,7 +2910,6 @@ function handleRoomClientEvents() {
|
||||
show(stopVideoButton);
|
||||
setColor(startVideoButton, 'red');
|
||||
setVideoButtonsDisabled(false);
|
||||
switchVideoMirror.disabled = false;
|
||||
// if (isParticipantsListOpen) getRoomParticipants();
|
||||
});
|
||||
rc.on(RoomClient.EVENTS.pauseVideo, () => {
|
||||
@@ -2926,7 +2932,6 @@ function handleRoomClientEvents() {
|
||||
show(startVideoButton);
|
||||
setVideoButtonsDisabled(false);
|
||||
isVideoPrivacyActive = false;
|
||||
switchVideoMirror.disabled = true;
|
||||
// if (isParticipantsListOpen) getRoomParticipants();
|
||||
});
|
||||
rc.on(RoomClient.EVENTS.startScreen, () => {
|
||||
@@ -4458,7 +4463,7 @@ function showAbout() {
|
||||
imageUrl: image.about,
|
||||
customClass: { image: 'img-about' },
|
||||
position: 'center',
|
||||
title: 'WebRTC SFU v1.5.74',
|
||||
title: 'WebRTC SFU v1.5.75',
|
||||
html: `
|
||||
<br />
|
||||
<div id="about">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* @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
|
||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||
* @version 1.5.74
|
||||
* @version 1.5.75
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -45,6 +45,7 @@ const html = {
|
||||
videoPrivacy: 'far fa-circle',
|
||||
expand: 'fas fa-ellipsis-vertical',
|
||||
hideALL: 'fas fa-eye',
|
||||
mirror: 'fas fa-arrow-right-arrow-left',
|
||||
};
|
||||
|
||||
const icons = {
|
||||
@@ -1406,6 +1407,10 @@ class RoomClient {
|
||||
if (type == mediaType.video) this.videoProducerId = producer.id;
|
||||
if (type == mediaType.screen) this.screenProducerId = producer.id;
|
||||
elem = await this.handleProducer(producer.id, type, stream);
|
||||
// No mirror effect for producer
|
||||
if (!isInitVideoMirror && elem.classList.contains('mirror')) {
|
||||
elem.classList.toggle('mirror');
|
||||
}
|
||||
//if (!screen && !isEnumerateDevices) enumerateVideoDevices(stream);
|
||||
} else {
|
||||
this.localAudioStream = stream;
|
||||
@@ -1882,7 +1887,7 @@ class RoomClient {
|
||||
}
|
||||
|
||||
async handleProducer(id, type, stream) {
|
||||
let elem, vb, vp, ts, d, p, i, au, pip, fs, pm, pb, pn;
|
||||
let elem, vb, vp, ts, d, p, i, au, pip, fs, pm, pb, pn, mv;
|
||||
switch (type) {
|
||||
case mediaType.video:
|
||||
case mediaType.screen:
|
||||
@@ -1914,6 +1919,9 @@ class RoomClient {
|
||||
ts = document.createElement('button');
|
||||
ts.id = id + '__snapshot';
|
||||
ts.className = html.snapshot;
|
||||
mv = document.createElement('button');
|
||||
mv.id = id + '__mirror';
|
||||
mv.className = html.mirror;
|
||||
pn = document.createElement('button');
|
||||
pn.id = id + '__pin';
|
||||
pn.className = html.pin;
|
||||
@@ -1945,6 +1953,7 @@ class RoomClient {
|
||||
BUTTONS.producerVideo.videoPictureInPicture &&
|
||||
this.isVideoPictureInPictureSupported &&
|
||||
vb.appendChild(pip);
|
||||
BUTTONS.producerVideo.videoMirrorButton && vb.appendChild(mv);
|
||||
BUTTONS.producerVideo.fullScreenButton && this.isVideoFullScreenSupported && vb.appendChild(fs);
|
||||
if (!this.isMobileDevice) vb.appendChild(pn);
|
||||
d.appendChild(elem);
|
||||
@@ -1959,6 +1968,7 @@ class RoomClient {
|
||||
this.isVideoFullScreenSupported && this.handleFS(elem.id, fs.id);
|
||||
this.handleDD(elem.id, this.peer_id, true);
|
||||
this.handleTS(elem.id, ts.id);
|
||||
this.handleMV(elem.id, mv.id);
|
||||
this.handlePN(elem.id, pn.id, d.id, isScreen);
|
||||
this.handleZV(elem.id, d.id, this.peer_id);
|
||||
if (!isScreen) this.handleVP(elem.id, vp.id);
|
||||
@@ -1968,6 +1978,7 @@ class RoomClient {
|
||||
handleAspectRatio();
|
||||
if (!this.isMobileDevice) {
|
||||
this.setTippy(pn.id, 'Toggle Pin', 'bottom');
|
||||
this.setTippy(mv.id, 'Toggle mirror', 'bottom');
|
||||
this.setTippy(pip.id, 'Toggle picture in picture', 'bottom');
|
||||
this.setTippy(ts.id, 'Snapshot', 'bottom');
|
||||
this.setTippy(vp.id, 'Toggle video privacy', 'bottom');
|
||||
@@ -2262,7 +2273,7 @@ class RoomClient {
|
||||
}
|
||||
|
||||
async handleConsumer(id, type, stream, peer_name, peer_info) {
|
||||
let elem, vb, d, p, i, cm, au, pip, fs, ts, sf, sm, sv, gl, ban, ko, pb, pm, pv, pn, ha;
|
||||
let elem, vb, d, p, i, cm, au, pip, fs, ts, sf, sm, sv, gl, ban, ko, pb, pm, pv, pn, ha, mv;
|
||||
|
||||
let eDiv, eBtn, eVc; // expand buttons
|
||||
|
||||
@@ -2311,6 +2322,9 @@ class RoomClient {
|
||||
pip = document.createElement('button');
|
||||
pip.id = id + '__pictureInPicture';
|
||||
pip.className = html.pip;
|
||||
mv = document.createElement('button');
|
||||
mv.id = id + '__videoMirror';
|
||||
mv.className = html.mirror;
|
||||
fs = document.createElement('button');
|
||||
fs.id = id + '__fullScreen';
|
||||
fs.className = html.fullScreen;
|
||||
@@ -2379,6 +2393,7 @@ class RoomClient {
|
||||
BUTTONS.consumerVideo.videoPictureInPicture &&
|
||||
this.isVideoPictureInPictureSupported &&
|
||||
vb.appendChild(pip);
|
||||
BUTTONS.consumerVideo.videoMirrorButton && vb.appendChild(mv);
|
||||
BUTTONS.consumerVideo.fullScreenButton && this.isVideoFullScreenSupported && vb.appendChild(fs);
|
||||
BUTTONS.consumerVideo.focusVideoButton && vb.appendChild(ha);
|
||||
if (!this.isMobileDevice) vb.appendChild(pn);
|
||||
@@ -2393,6 +2408,7 @@ class RoomClient {
|
||||
this.isVideoFullScreenSupported && this.handleFS(elem.id, fs.id);
|
||||
this.handleDD(elem.id, remotePeerId);
|
||||
this.handleTS(elem.id, ts.id);
|
||||
this.handleMV(elem.id, mv.id);
|
||||
this.handleSF(sf.id);
|
||||
this.handleHA(ha.id, d.id);
|
||||
this.handleSM(sm.id, peer_name);
|
||||
@@ -2425,6 +2441,7 @@ class RoomClient {
|
||||
this.setTippy(pn.id, 'Toggle Pin', 'bottom');
|
||||
this.setTippy(ha.id, 'Toggle Focus mode', 'bottom');
|
||||
this.setTippy(pip.id, 'Toggle picture in picture', 'bottom');
|
||||
this.setTippy(mv.id, 'Toggle mirror', 'bottom');
|
||||
this.setTippy(ts.id, 'Snapshot', 'bottom');
|
||||
this.setTippy(sf.id, 'Send file', 'bottom');
|
||||
this.setTippy(sm.id, 'Send message', 'bottom');
|
||||
@@ -3610,6 +3627,21 @@ class RoomClient {
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################################
|
||||
// HANDLE VIDEO MIRROR
|
||||
// ####################################################
|
||||
|
||||
handleMV(elemId, tsId) {
|
||||
let videoPlayer = this.getId(elemId);
|
||||
let btnMv = this.getId(tsId);
|
||||
if (btnMv && videoPlayer) {
|
||||
btnMv.addEventListener('click', () => {
|
||||
videoPlayer.classList.toggle('mirror');
|
||||
//rc.roomMessage('toggleVideoMirror', videoPlayer.classList.contains('mirror'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ####################################################
|
||||
// VIDEO CIRCLE - PRIVACY MODE
|
||||
// ####################################################
|
||||
|
||||
@@ -48,6 +48,7 @@ let BUTTONS = {
|
||||
},
|
||||
producerVideo: {
|
||||
videoPictureInPicture: true,
|
||||
videoMirrorButton: true,
|
||||
fullScreenButton: true,
|
||||
snapShotButton: true,
|
||||
muteAudioButton: true,
|
||||
@@ -55,6 +56,7 @@ let BUTTONS = {
|
||||
},
|
||||
consumerVideo: {
|
||||
videoPictureInPicture: true,
|
||||
videoMirrorButton: true,
|
||||
fullScreenButton: true,
|
||||
snapShotButton: true,
|
||||
focusVideoButton: true,
|
||||
|
||||
@@ -170,6 +170,7 @@ access to use this app.
|
||||
<button id="initAudioVideoButton" class="fas fa-eye"></button>
|
||||
<button id="initStartScreenButton" class="fas fa-desktop hidden"></button>
|
||||
<button id="initStopScreenButton" class="fas fa-stop-circle hidden"></button>
|
||||
<button id="initVideoMirrorButton" class="fas fa-arrow-right-arrow-left"></button>
|
||||
<select id="initVideoSelect" class="form-select text-light bg-dark"></select>
|
||||
<select id="initMicrophoneSelect" class="form-select text-light bg-dark"></select>
|
||||
<select id="initSpeakerSelect" class="form-select text-light bg-dark"></select>
|
||||
@@ -378,15 +379,6 @@ access to use this app.
|
||||
<p>Video Source:</p>
|
||||
</div>
|
||||
<select id="videoSelect" class="form-select text-light bg-dark"></select>
|
||||
<div id="VideoMirrorDiv">
|
||||
<div class="title">
|
||||
<i class="fas fa-arrow-right-arrow-left"></i>
|
||||
<p>Toggle mirror</p>
|
||||
<div class="form-check form-switch form-switch-md">
|
||||
<input id="switchVideoMirror" class="form-check-input" type="checkbox" checked />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="title">
|
||||
<i class="fas fa-palette"></i>
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم