1515 * @license For commercial use or closed source, contact us at license.mirotalk@gmail.com or purchase directly from CodeCanyon
1616 * @license CodeCanyon: https://codecanyon.net/item/mirotalk-p2p-webrtc-realtime-video-conferences/38376661
1717 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com
18- * @version 1.8.33
18+ * @version 1.8.34
1919 *
2020 */
2121
@@ -2893,13 +2893,16 @@ async function handleOnTrack(peer_id, peers) {
28932893 // Helper to load or attach stream
28942894 const handleStream = (elementId, streamType) => {
28952895 const element = getId(`${peer_id}___${elementId}`);
2896- const hasStream = element?.srcObject && (elementId === 'audio' || hasVideoTrack(element.srcObject));
28972896
2898- if (!hasStream) {
2897+ if (!element) {
2898+ // Tile doesn't exist yet — create everything
28992899 loadRemoteMediaStream(inbound, allPeers || peers, peer_id, streamType);
29002900 } else {
2901+ // Tile already exists (e.g. peer joined with camera off) — just attach the new stream
29012902 attachMediaStream(element, inbound);
29022903 elemDisplay(element, true, 'block');
2904+ // Safari requires an explicit play() after srcObject is reassigned
2905+ element.play().catch(() => {});
29032906 }
29042907 };
29052908
@@ -2908,12 +2911,11 @@ async function handleOnTrack(peer_id, peers) {
29082911
29092912 if (audioElement) {
29102913 attachMediaStream(audioElement, inbound);
2911- if (!audioElement.srcObject) {
2912- audioElement.play().catch((err) => {
2913- console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
2914- handleAudioFallback(audioElement, peer_name);
2915- });
2916- }
2914+ // Always call play() — srcObject was just assigned so the old check (!srcObject) was always false
2915+ audioElement.play().catch((err) => {
2916+ console.warn('[AUDIO] Autoplay not allowed by device, setting up fallback:', err);
2917+ handleAudioFallback(audioElement, peer_name);
2918+ });
29172919 } else {
29182920 loadRemoteMediaStream(inbound, allPeers || peers, peer_id, 'audio');
29192921 }
@@ -4779,6 +4781,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
47794781 remoteMedia.setAttribute('id', peer_id + '___video');
47804782 remoteMedia.setAttribute('playsinline', true);
47814783 remoteMedia.autoplay = true;
4784+ remoteMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
47824785 remoteMediaControls = isMobileDevice ? false : remoteMediaControls;
47834786 remoteMedia.style.objectFit = 'var(--video-object-fit)';
47844787 remoteMedia.style.name = peer_id + '_typeCam';
@@ -4804,6 +4807,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
48044807 videoMediaContainer.appendChild(remoteVideoWrap);
48054808 // attachMediaStream is a part of the adapter.js library
48064809 attachMediaStream(remoteMedia, stream);
4810+ // Explicitly play – required on mobile Safari where autoplay alone is not enough
4811+ remoteMedia.play().catch(() => {});
48074812
48084813 // resize video elements
48094814 adaptAspectRatio();
@@ -4997,6 +5002,7 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
49975002 remoteScreenMedia.setAttribute('id', peer_id + '___screen');
49985003 remoteScreenMedia.setAttribute('playsinline', true);
49995004 remoteScreenMedia.autoplay = true;
5005+ remoteScreenMedia.muted = true; // audio is handled by a separate <audio> element; muting allows autoplay on Safari
50005006 remoteScreenMedia.controls = remoteMediaControls;
50015007 remoteScreenMedia.style.objectFit = 'contain';
50025008 remoteScreenMedia.style.name = peer_id + '_typeScreen';
@@ -5017,6 +5023,8 @@ async function loadRemoteMediaStream(stream, peers, peer_id, kind) {
50175023
50185024 videoMediaContainer.appendChild(remoteScreenWrap);
50195025 attachMediaStream(remoteScreenMedia, stream);
5026+ // Explicitly play – required on mobile Safari where autoplay alone is not enough
5027+ remoteScreenMedia.play().catch(() => {});
50205028 adaptAspectRatio();
50215029
50225030 // handle remote private messages
@@ -12852,6 +12860,8 @@ function setPeerVideoStatus(peer_id, status) {
1285212860 { element: peerVideoPlayer, display: true, mode: 'block' },
1285312861 { element: peerVideoAvatarImage, display: false },
1285412862 ]);
12863+ // Safari requires explicit play() when a video element becomes visible again
12864+ if (peerVideoPlayer) peerVideoPlayer.play().catch(() => {});
1285512865 if (peerVideoStatus) {
1285612866 setMediaButtonsClass([{ element: peerVideoStatus, status: true, mediaType: 'video' }]);
1285712867 setTippy(peerVideoStatus, 'Participant video is on', 'bottom');
@@ -15813,7 +15823,7 @@ function showAbout() {
1581315823 Swal.fire({
1581415824 background: swBg,
1581515825 position: 'center',
15816- title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.33 ',
15826+ title: brand.about?.title && brand.about.title.trim() !== '' ? brand.about.title : 'WebRTC P2P v1.8.34 ',
1581715827 imageUrl: brand.about?.imageUrl && brand.about.imageUrl.trim() !== '' ? brand.about.imageUrl : images.about,
1581815828 customClass: { image: 'img-about' },
1581915829 html: renderRoomTemplate('tpl-about-modal', {
0 commit comments