[mirotalk] - fix: resolve Safari autoplay, duplicate tile, and audio re-attach issues on mobile
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# ====================================================
|
# ====================================================
|
||||||
# MiroTalk P2P v.1.8.33 - Environment Configuration
|
# MiroTalk P2P v.1.8.34 - Environment Configuration
|
||||||
# ====================================================
|
# ====================================================
|
||||||
|
|
||||||
# App environment
|
# App environment
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* ==============================================
|
* ==============================================
|
||||||
* MiroTalk P2P v.1.8.33 - Configuration File
|
* MiroTalk P2P v.1.8.34 - Configuration File
|
||||||
* ==============================================
|
* ==============================================
|
||||||
*
|
*
|
||||||
* This file is the central configuration source.
|
* This file is the central configuration source.
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ dependencies: {
|
|||||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
* @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
|
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||||
* @version 1.8.33
|
* @version 1.8.34
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mirotalk",
|
"name": "mirotalk",
|
||||||
"version": "1.8.33",
|
"version": "1.8.34",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mirotalk",
|
"name": "mirotalk",
|
||||||
"version": "1.8.33",
|
"version": "1.8.34",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mattermost/client": "11.6.0",
|
"@mattermost/client": "11.6.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mirotalk",
|
"name": "mirotalk",
|
||||||
"version": "1.8.33",
|
"version": "1.8.34",
|
||||||
"description": "A free WebRTC browser-based video call",
|
"description": "A free WebRTC browser-based video call",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+1
-1
@@ -109,7 +109,7 @@ let brand = {
|
|||||||
},
|
},
|
||||||
about: {
|
about: {
|
||||||
imageUrl: '../images/mirotalk-logo.gif',
|
imageUrl: '../images/mirotalk-logo.gif',
|
||||||
title: 'WebRTC P2P v1.8.33',
|
title: 'WebRTC P2P v1.8.34',
|
||||||
html: `
|
html: `
|
||||||
<button
|
<button
|
||||||
id="support-button"
|
id="support-button"
|
||||||
|
|||||||
+20
-10
@@ -15,7 +15,7 @@
|
|||||||
* @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
|
* @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
|
* @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
|
||||||
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
|
||||||
* @version 1.8.33
|
* @version 1.8.34
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -2893,13 +2893,16 @@ async function handleOnTrack(peer_id, peers) {
|
|||||||
// Helper to load or attach stream
|
// Helper to load or attach stream
|
||||||
const handleStream = (elementId, streamType) => {
|
const handleStream = (elementId, streamType) => {
|
||||||
const element = getId(`${peer_id}___${elementId}`);
|
const element = getId(`${peer_id}___${elementId}`);
|
||||||
const hasStream = element?.srcObject && (elementId === 'audio' || hasVideoTrack(element.srcObject));
|
|
||||||
|
|
||||||
if (!hasStream) {
|
if (!element) {
|
||||||
|
// Tile doesn't exist yet — create everything
|
||||||
loadRemoteMediaStream(inbound, allPeers || peers, peer_id, streamType);
|
loadRemoteMediaStream(inbound, allPeers || peers, peer_id, streamType);
|
||||||
} else {
|
} else {
|
||||||
|
// Tile already exists (e.g. peer joined with camera off) — just attach the new stream
|
||||||
attachMediaStream(element, inbound);
|
attachMediaStream(element, inbound);
|
||||||
elemDisplay(element, true, 'block');
|
elemDisplay(element, true, 'block');
|
||||||
|
// Safari requires an explicit play() after srcObject is reassigned
|
||||||
|
element.play().catch(() => {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2908,12 +2911,11 @@ async function handleOnTrack(peer_id, peers) {
|
|||||||
|
|
||||||
if (audioElement) {
|
if (audioElement) {
|
||||||
attachMediaStream(audioElement, inbound);
|
attachMediaStream(audioElement, inbound);
|
||||||
if (!audioElement.srcObject) {
|
// Always call play() — srcObject was just assigned so the old check (!srcObject) was always false
|
||||||
audioElement.play().catch((err) => {
|
audioElement.play().catch((err) => {
|
||||||
console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
|
console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
|
||||||
handleAudioFallback(audioElement, peer_name);
|
handleAudioFallback(audioElement, peer_name);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
loadRemoteMediaStream(inbound, allPeers || peers, peer_id, 'audio');
|
loadRemoteMediaStream(inbound, allPeers || peers, peer_id, 'audio');
|
||||||
}
|
}
|
||||||
@@ -4779,6 +4781,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
|
|||||||
remoteMedia.setAttribute('id', peer_id + '___video');
|
remoteMedia.setAttribute('id', peer_id + '___video');
|
||||||
remoteMedia.setAttribute('playsinline', true);
|
remoteMedia.setAttribute('playsinline', true);
|
||||||
remoteMedia.autoplay = true;
|
remoteMedia.autoplay = true;
|
||||||
|
remoteMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
|
||||||
remoteMediaControls = isMobileDevice ? false : remoteMediaControls;
|
remoteMediaControls = isMobileDevice ? false : remoteMediaControls;
|
||||||
remoteMedia.style.objectFit = 'var(--video-object-fit)';
|
remoteMedia.style.objectFit = 'var(--video-object-fit)';
|
||||||
remoteMedia.style.name = peer_id + '_typeCam';
|
remoteMedia.style.name = peer_id + '_typeCam';
|
||||||
@@ -4804,6 +4807,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
|
|||||||
videoMediaContainer.appendChild(remoteVideoWrap);
|
videoMediaContainer.appendChild(remoteVideoWrap);
|
||||||
// attachMediaStream is a part of the adapter.js library
|
// attachMediaStream is a part of the adapter.js library
|
||||||
attachMediaStream(remoteMedia, stream);
|
attachMediaStream(remoteMedia, stream);
|
||||||
|
// Explicitly play – required on mobile Safari where autoplay alone is not enough
|
||||||
|
remoteMedia.play().catch(() => {});
|
||||||
|
|
||||||
// resize video elements
|
// resize video elements
|
||||||
adaptAspectRatio();
|
adaptAspectRatio();
|
||||||
@@ -4997,6 +5002,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
|
|||||||
remoteScreenMedia.setAttribute('id', peer_id + '___screen');
|
remoteScreenMedia.setAttribute('id', peer_id + '___screen');
|
||||||
remoteScreenMedia.setAttribute('playsinline', true);
|
remoteScreenMedia.setAttribute('playsinline', true);
|
||||||
remoteScreenMedia.autoplay = true;
|
remoteScreenMedia.autoplay = true;
|
||||||
|
remoteScreenMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
|
||||||
remoteScreenMedia.controls = remoteMediaControls;
|
remoteScreenMedia.controls = remoteMediaControls;
|
||||||
remoteScreenMedia.style.objectFit = 'contain';
|
remoteScreenMedia.style.objectFit = 'contain';
|
||||||
remoteScreenMedia.style.name = peer_id + '_typeScreen';
|
remoteScreenMedia.style.name = peer_id + '_typeScreen';
|
||||||
@@ -5017,6 +5023,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
|
|||||||
|
|
||||||
videoMediaContainer.appendChild(remoteScreenWrap);
|
videoMediaContainer.appendChild(remoteScreenWrap);
|
||||||
attachMediaStream(remoteScreenMedia, stream);
|
attachMediaStream(remoteScreenMedia, stream);
|
||||||
|
// Explicitly play – required on mobile Safari where autoplay alone is not enough
|
||||||
|
remoteScreenMedia.play().catch(() => {});
|
||||||
adaptAspectRatio();
|
adaptAspectRatio();
|
||||||
|
|
||||||
// handle remote private messages
|
// handle remote private messages
|
||||||
@@ -12852,6 +12860,8 @@ function setPeerVideoStatus(peer_id, status) {
|
|||||||
{ element: peerVideoPlayer, display: true, mode: 'block' },
|
{ element: peerVideoPlayer, display: true, mode: 'block' },
|
||||||
{ element: peerVideoAvatarImage, display: false },
|
{ element: peerVideoAvatarImage, display: false },
|
||||||
]);
|
]);
|
||||||
|
// Safari requires explicit play() when a video element becomes visible again
|
||||||
|
if (peerVideoPlayer) peerVideoPlayer.play().catch(() => {});
|
||||||
if (peerVideoStatus) {
|
if (peerVideoStatus) {
|
||||||
setMediaButtonsClass([{ element: peerVideoStatus, status: true, mediaType: 'video' }]);
|
setMediaButtonsClass([{ element: peerVideoStatus, status: true, mediaType: 'video' }]);
|
||||||
setTippy(peerVideoStatus, 'Participant video is on', 'bottom');
|
setTippy(peerVideoStatus, 'Participant video is on', 'bottom');
|
||||||
@@ -15813,7 +15823,7 @@ function showAbout() {
|
|||||||
Swal.fire({
|
Swal.fire({
|
||||||
background: swBg,
|
background: swBg,
|
||||||
position: 'center',
|
position: 'center',
|
||||||
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.33',
|
title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.34',
|
||||||
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
|
imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
|
||||||
customClass: { image: 'img-about' },
|
customClass: { image: 'img-about' },
|
||||||
html: renderRoomTemplate('tpl-about-modal', {
|
html: renderRoomTemplate('tpl-about-modal', {
|
||||||
|
|||||||
Reference in New Issue
Block a user