Files
video_socket-server/client/public/call/media/renderer-media.js

192 lines
5.9 KiB
JavaScript
Raw Normal View History

2026-05-25 20:37:36 +08:00
import { createParticipantTile, getParticipantTile } from '../participants/renderer-participant-grid.js';
import { createLogger } from '../../shared/logger.js';
2026-05-24 14:16:28 +08:00
const logger = createLogger('renderer-media');
2026-05-24 13:56:53 +08:00
export function getVideoResolution(track) {
if (track && track.getSettings) {
const settings = track.getSettings();
return {
width: settings.width || 640,
height: settings.height || 480
};
}
return { width: 640, height: 480 };
}
export function adjustVideoSize(videoElement) {
if (!videoElement) return;
const container = videoElement.parentElement;
if (!container) return;
videoElement.style.transform = 'translateZ(0)';
videoElement.style.willChange = 'transform';
container.style.display = 'flex';
container.style.alignItems = 'center';
container.style.justifyContent = 'center';
videoElement.style.imageRendering = 'auto';
videoElement.style.maxWidth = '100%';
videoElement.style.maxHeight = '100%';
videoElement.style.objectFit = 'contain';
}
export function renderParticipantStreamMedia({
grid,
stream,
connectionId,
displayName,
getGridTemplateColumns,
remoteVideo,
connectingOverlay,
remoteVideoPlaceholder
}) {
if (!grid) return;
grid.classList.remove('hidden');
let tile = getParticipantTile(grid, connectionId);
if (!tile) {
tile = createParticipantTile(connectionId, displayName);
grid.appendChild(tile);
2026-05-24 14:16:28 +08:00
logger.debug(`Created participant video tile for ${connectionId}`);
2026-05-24 13:56:53 +08:00
}
const video = tile.querySelector('video');
if (video && stream) {
if (video.srcObject === stream) {
2026-05-24 14:16:28 +08:00
logger.debug(`Same stream for participant ${connectionId}, ensuring playback`);
video.play().catch(error => logger.debug('Auto-play prevented:', error.message));
2026-05-24 13:56:53 +08:00
} else {
video.srcObject = stream;
2026-05-24 14:16:28 +08:00
video.play().catch(error => logger.debug('Auto-play prevented:', error.message));
logger.debug(`Set remote stream for participant tile ${connectionId}`);
2026-05-24 13:56:53 +08:00
}
}
const remoteVideoContainer = remoteVideo?.closest('.absolute.inset-0.video-fade-in');
if (remoteVideoContainer) {
remoteVideoContainer.classList.add('hidden');
}
const tileCount = grid.querySelectorAll('[data-participant-id]').length;
grid.style.gridTemplateColumns = getGridTemplateColumns(tileCount);
if (connectingOverlay) {
connectingOverlay.classList.add('hidden');
}
if (remoteVideoPlaceholder) {
remoteVideoPlaceholder.classList.add('hidden');
}
}
export function renderSingleRemoteStreamMedia({
remoteVideo,
stream,
disconnectedOverlay,
remoteVideoPlaceholder,
connectingOverlay
}) {
if (!remoteVideo || !stream) {
2026-05-24 14:16:28 +08:00
logger.error('Either remoteVideo element or stream is missing');
2026-05-24 13:56:53 +08:00
return;
}
2026-05-24 14:16:28 +08:00
logger.debug('Rendering remote stream:', stream, 'tracks:', stream.getTracks().map(track => `${track.kind}(${track.readyState})`));
2026-05-24 13:56:53 +08:00
if (remoteVideo.srcObject === stream) {
2026-05-24 14:16:28 +08:00
logger.debug('Same stream object, track added - ensuring playback');
remoteVideo.play().catch(error => logger.debug('Auto-play prevented:', error.message));
2026-05-24 13:56:53 +08:00
return;
}
remoteVideo.srcObject = stream;
remoteVideo.autoplay = true;
remoteVideo.playsinline = true;
remoteVideo.muted = false;
remoteVideo.play().catch(error => {
2026-05-24 14:16:28 +08:00
logger.debug('Auto-play prevented, will retry on interaction:', error.message);
2026-05-24 13:56:53 +08:00
});
if (disconnectedOverlay) {
disconnectedOverlay.classList.add('hidden');
}
const videoTracks = stream.getVideoTracks();
const audioTracks = stream.getAudioTracks();
2026-05-24 14:16:28 +08:00
logger.debug(`Stream has ${videoTracks.length} video tracks, ${audioTracks.length} audio tracks`);
2026-05-24 13:56:53 +08:00
if (videoTracks.length === 0) {
2026-05-24 14:16:28 +08:00
logger.debug('Audio-only stream, waiting for video track...');
2026-05-24 13:56:53 +08:00
return;
}
if (remoteVideoPlaceholder) {
remoteVideoPlaceholder.classList.add('hidden');
}
if (connectingOverlay) {
connectingOverlay.classList.add('hidden');
}
const activeVideoTrack = videoTracks.find(track => track.readyState === 'live');
if (!activeVideoTrack) return;
adjustVideoSize(remoteVideo, getVideoResolution(activeVideoTrack));
activeVideoTrack.addEventListener('resize', () => {
adjustVideoSize(remoteVideo, getVideoResolution(activeVideoTrack));
});
}
export function clearParticipantGrid(grid) {
if (!grid) return;
grid.querySelectorAll('[data-participant-id]').forEach(tile => {
const video = tile.querySelector('video');
if (video) {
video.srcObject = null;
}
tile.remove();
});
grid.classList.add('hidden');
}
export function removeParticipantTile({
grid,
connectionId,
getGridTemplateColumns,
remoteVideo,
remoteVideoPlaceholder,
remoteNetworkIndicator
}) {
if (!grid) return;
const tile = getParticipantTile(grid, connectionId);
if (tile) {
const video = tile.querySelector('video');
if (video) {
video.srcObject = null;
}
tile.remove();
2026-05-24 14:16:28 +08:00
logger.debug(`Removed participant video tile for ${connectionId}`);
2026-05-24 13:56:53 +08:00
}
const remainingTiles = grid.querySelectorAll('[data-participant-id]');
if (remainingTiles.length === 0) {
grid.classList.add('hidden');
const remoteVideoContainer = remoteVideo?.closest('.absolute.inset-0.video-fade-in');
if (remoteVideoContainer) {
remoteVideoContainer.classList.remove('hidden');
}
if (remoteVideoPlaceholder) {
remoteVideoPlaceholder.classList.remove('hidden');
}
} else {
grid.style.gridTemplateColumns = getGridTemplateColumns(remainingTiles.length);
}
if (remoteNetworkIndicator) {
remoteNetworkIndicator.className = 'w-2 h-2 bg-gray-500 rounded-full';
}
}