[mirotalk] - fix: resolve Safari autoplay, duplicate tile, and audio re-attach issues on mobile

This commit is contained in:
Miroslav Pejic
2026-05-07 00:36:12 +02:00
parent 345ecef173
commit 8748273842
7 changed files with 27 additions and 17 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# ====================================================
# MiroTalk P2P v.1.8.33 - Environment Configuration
# MiroTalk P2P v.1.8.34 - Environment Configuration
# ====================================================
# App environment
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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 CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
* @version 1.8.33
* @version 1.8.34
*
*/
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "mirotalk",
"version": "1.8.33",
"version": "1.8.34",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mirotalk",
"version": "1.8.33",
"version": "1.8.34",
"license": "AGPL-3.0",
"dependencies": {
"@mattermost/client": "11.6.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mirotalk",
"version": "1.8.33",
"version": "1.8.34",
"description": "A free WebRTC browser-based video call",
"main": "server.js",
"scripts": {
+1 -1
View File
@@ -109,7 +109,7 @@ let brand = {
},
about: {
imageUrl: '../images/mirotalk-logo.gif',
title: 'WebRTC P2P v1.8.33',
title: 'WebRTC P2P v1.8.34',
html: `
<button
id="support-button"
+20 -10
View File
@@ -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.8.33
* @version 1.8.34
*
*/
@@ -2893,13 +2893,16 @@ async function handleOnTrack(peer_id, peers) {
// Helper to load or attach stream
const handleStream = (elementId, streamType) => {
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);
} else {
// Tile already exists (e.g. peer joined with camera off) — just attach the new stream
attachMediaStream(element, inbound);
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) {
attachMediaStream(audioElement, inbound);
if (!audioElement.srcObject) {
audioElement.play().catch((err) => {
console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
handleAudioFallback(audioElement, peer_name);
});
}
// Always call play() — srcObject was just assigned so the old check (!srcObject) was always false
audioElement.play().catch((err) => {
console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
handleAudioFallback(audioElement, peer_name);
});
} else {
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('playsinline', true);
remoteMedia.autoplay = true;
remoteMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
remoteMediaControls = isMobileDevice ? false : remoteMediaControls;
remoteMedia.style.objectFit = 'var(--video-object-fit)';
remoteMedia.style.name = peer_id + '_typeCam';
@@ -4804,6 +4807,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
videoMediaContainer.appendChild(remoteVideoWrap);
// attachMediaStream is a part of the adapter.js library
attachMediaStream(remoteMedia, stream);
// Explicitly play required on mobile Safari where autoplay alone is not enough
remoteMedia.play().catch(() => {});
// resize video elements
adaptAspectRatio();
@@ -4997,6 +5002,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
remoteScreenMedia.setAttribute('id', peer_id + '___screen');
remoteScreenMedia.setAttribute('playsinline', true);
remoteScreenMedia.autoplay = true;
remoteScreenMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
remoteScreenMedia.controls = remoteMediaControls;
remoteScreenMedia.style.objectFit = 'contain';
remoteScreenMedia.style.name = peer_id + '_typeScreen';
@@ -5017,6 +5023,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
videoMediaContainer.appendChild(remoteScreenWrap);
attachMediaStream(remoteScreenMedia, stream);
// Explicitly play required on mobile Safari where autoplay alone is not enough
remoteScreenMedia.play().catch(() => {});
adaptAspectRatio();
// handle remote private messages
@@ -12852,6 +12860,8 @@ function setPeerVideoStatus(peer_id, status) {
{ element: peerVideoPlayer, display: true, mode: 'block' },
{ element: peerVideoAvatarImage, display: false },
]);
// Safari requires explicit play() when a video element becomes visible again
if (peerVideoPlayer) peerVideoPlayer.play().catch(() => {});
if (peerVideoStatus) {
setMediaButtonsClass([{ element: peerVideoStatus, status: true, mediaType: 'video' }]);
setTippy(peerVideoStatus, 'Participant video is on', 'bottom');
@@ -15813,7 +15823,7 @@ function showAbout() {
Swal.fire({
background: swBg,
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,
customClass: { image: 'img-about' },
html: renderRoomTemplate('tpl-about-modal', {