diff --git a/README.md b/README.md
index 82ddd607..f7627c9b 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@
- File sharing with drag-and-drop support.
- Choose your audio input, output, and video source.
- Supports video quality up to 8K and 60 FPS.
-- Supports advance Picture-in-Picture (PiP) offering a more streamlined and flexible viewing experience.
+- Supports advance Video/Document Picture-in-Picture (PiP) offering a more streamlined and flexible viewing experience.
- Record your screen, audio, and video.
- Snapshot video frames and save them as PNG images.
- Chat with an Emoji Picker for expressing feelings, private messages, Markdown support, and conversation saving.
diff --git a/app/src/server.js b/app/src/server.js
index c084fe92..e7d8d9a5 100755
--- a/app/src/server.js
+++ b/app/src/server.js
@@ -39,7 +39,7 @@ dependencies: {
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
- * @version 1.4.45
+ * @version 1.4.46
*
*/
diff --git a/package.json b/package.json
index a39df398..10904e09 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
- "version": "1.4.45",
+ "version": "1.4.46",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
diff --git a/public/js/client.js b/public/js/client.js
index 951c90d3..7e6ab9b6 100644
--- a/public/js/client.js
+++ b/public/js/client.js
@@ -15,7 +15,7 @@
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
- * @version 1.4.45
+ * @version 1.4.46
*
*/
@@ -5162,6 +5162,18 @@ function setDocumentPiPBtn() {
});
}
+/**
+ * Restart documentPictureInPicture
+ * @returns void
+ */
+async function documentPictureInPictureRestart() {
+ if (!showDocumentPipBtn || !documentPictureInPicture.window) return;
+ documentPictureInPictureClose();
+ setTimeout(async () => {
+ await documentPictureInPictureOpen();
+ }, 300);
+}
+
/**
* Close documentPictureInPicture
*/
@@ -5217,35 +5229,19 @@ async function documentPictureInPictureOpen() {
// No video stream detected or is video share from URL...
if (!video.srcObject || video.id === 'videoAudioUrlElement') return;
- let videoPIPAllowed = false;
-
// get video element
const videoPlayer = getId(video.id);
- // Check if video can be add on pipVideo
- if (video.id === 'myVideo') {
- const localVideoStatus = getId('myVideoStatusIcon');
-
- videoPIPAllowed =
- localVideoStatus.className === className.videoOn && // video is ON
- !videoPlayer.classList.contains('videoCircle'); // not in privacy mode
-
- console.log('DOCUMENT PIP LOCAL videoPIPAllowed -----> ' + videoPIPAllowed);
- } else {
- const parts = video.id.split('___'); // peerId___video
- const peer_id = parts[0];
- const remoteVideoStatus = getId(peer_id + '_videoStatus');
-
- videoPIPAllowed =
- remoteVideoStatus.className === className.videoOn && // video is ON
- !videoPlayer.classList.contains('videoCircle'); // not in privacy mode
+ const isPIPAllowed = !videoPlayer.classList.contains('videoCircle'); // not in privacy mode
- console.log('DOCUMENT PIP REMOTE videoPIPAllowed -----> ' + videoPIPAllowed);
- }
+ // Check if video can be add on pipVideo
+ video.id === 'myVideo'
+ ? console.log('DOCUMENT PIP LOCAL: PiP allowed? -----> ' + isPIPAllowed)
+ : console.log('DOCUMENT PIP REMOTE: PiP allowed? -----> ' + isPIPAllowed);
- if (!videoPIPAllowed) return;
+ if (!isPIPAllowed) return;
- // Video is ON not in privacy mode continue....
+ // Video is ON and not in privacy mode continue....
foundVideo = true;
@@ -5258,6 +5254,19 @@ async function documentPictureInPictureOpen() {
pipVideo.muted = true;
pipVideoContainer.append(pipVideo);
+
+ const videoElementObserver = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
+ // Handle class changes in video elements
+ console.log(`Video ${mutation.target.id} class changed:`, mutation.target.className);
+ cloneVideoElements();
+ }
+ });
+ });
+
+ // Start observing for new videos and class changes
+ videoElementObserver.observe(video, { attributes: true, attributeFilter: ['class'] });
});
return foundVideo;
@@ -5551,7 +5560,7 @@ function setupMySettings() {
videoSelect.addEventListener('change', async () => {
await changeLocalCamera(videoSelect.value);
await handleLocalCameraMirror();
- await documentPictureInPictureClose();
+ await documentPictureInPictureRestart();
refreshLsDevices();
});
// select video quality
@@ -6427,12 +6436,9 @@ async function handleVideo(e, init, force = null) {
if (!videoStatus) {
if (!isScreenStreaming) {
// Stop the video track based on the condition
- if (init) {
- await stopVideoTracks(initStream); // Stop init video track (camera LED off)
- } else {
- await stopVideoTracks(localVideoMediaStream); // Stop local video track (camera LED off)
- await documentPictureInPictureClose(); // Close doc PIP if open
- }
+ init
+ ? await stopVideoTracks(initStream) // Stop init video track (camera LED off)
+ : await stopVideoTracks(localVideoMediaStream); // Stop local video track (camera LED off)
}
} else {
if (init) {
@@ -6441,6 +6447,7 @@ async function handleVideo(e, init, force = null) {
} else if (!isScreenStreaming) {
// Resume the video track for the local camera (camera LED on)
await changeLocalCamera(videoSelect.value);
+ await documentPictureInPictureRestart(); // Restart doc PIP if open
}
}
@@ -11017,7 +11024,7 @@ function showAbout() {
Swal.fire({
background: swBg,
position: 'center',
- title: 'WebRTC P2P v1.4.45',
+ title: 'WebRTC P2P v1.4.46',
imageAlt: 'mirotalk-about',
imageUrl: images.about,
customClass: { image: 'img-about' },