Reputation: 1
I’m using React Three Fiber and THREE.VideoTexture to display videos as textures. However, some videos trigger frequent "stalled" events, especially in Safari, while not so much in Chrome.
I suspect this issue is related to how the video is being served or its encoding, but I’m struggling to pinpoint the root cause. When I open the video URL directly in the browser, it plays fine, which makes me think the issue lies somewhere between loading it in the WebGL context and the way it’s handled by Safari.
Here’s how I’m setting basicaly loading the video
const videoTexture = useMemo(() => {
if (!videoSrc) return null;
// Create video element
const video = document.createElement('video');
video.src = videoSrc;
video.crossOrigin = 'anonymous'; // Allow CORS if necessary
video.loop = false;
video.muted = true; // Required for autoplay in some browsers
video.preload = 'auto';
video.setAttribute('playsinline', '');
video.setAttribute('webkit-playsinline', ''); // iOS-specific
// Create a Three.js VideoTexture
const texture = new THREE.VideoTexture(video);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBAFormat;
return texture;
}, [videoSrc]);
useEffect(() => {
if (!videoTexture) return;
const handleCanPlay = () => {
console.log('Video loaded successfully.', videoTexture.image.src);
};
const handleError = (e) => {
console.error('Video encountered an error.', videoTexture.image.src, e);
retryLoading();
};
const handleStalled = (e) => {
console.warn('Video loading stalled.', videoTexture.image.src, e);
retryLoading();
};
const retryLoading = () => {
videoTexture.image.load();
};
// Event Listeners
videoTexture.image.addEventListener('canplaythrough', handleCanPlay);
videoTexture.image.addEventListener('error', handleError);
videoTexture.image.addEventListener('stalled', handleStalled);
videoTexture.image.load();
// Cleanup
return () => {
videoTexture.image.preload = 'none';
videoTexture.image.removeEventListener('canplaythrough', handleCanPlay);
videoTexture.image.removeEventListener('error', handleError);
videoTexture.image.removeEventListener('stalled', handleStalled);
};
}, [videoTexture]);
And sometime I get Video loading stalled. (even for 5-6 times before I get Video loaded successfully.)
Here’s the encoding info for a video that stalls often:
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf61.7.100
Duration: 00:00:33.60, start: 0.000000, bitrate: 814 kb/s
Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 960x720 [SAR 1:1 DAR 4:3], 812 kb/s, 30 fps, 30 tbr, 90k tbn (default)
Metadata:
handler_name : VideoHandler
I’m serving the video using Nginx, with the following configuration:
location ~* /assets/video/ {
expires 30d;
add_header Cache-Control "public";
add_header Accept-Ranges bytes;
# Proper MIME type for MP4 files
types { video/mp4 mp4; }
# Optimize file serving
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Enable byte-range requests for video seeking
aio threads;
output_buffers 1 64k;
directio 512k;
}
Why does Safari trigger so many "stalled" events? Are there best practices for serving videos efficiently in a WebGL/React Three Fiber context? Could this be an issue with video encoding, MIME types, or how it's being served by Nginx? Any insights or suggestions would be greatly appreciated!
Keep in mind that I can not host the videos in CDN so of course the approch is not optimal but still it should be stable enough.
Upvotes: 0
Views: 26