Background
I have a video player, an instance of VideoJS. The player receives videoData via a socket event video_data from the server. Then it changes the video source to the one that is received.
The server is running on-premise, all video files, database is hosted on-premise. So there is close to 0 network latency.
The problem
A black screen appears while the browser loads the video data. I would like to tune it further and not show the black screen at all.
My solution
Here is my approach: I want to create two video instances (<video> tags) and switch between them. The first one is hidden and the second one is visible. When it receives video data, rather than changing the source of the current visible <video> tag, it changes the source of the hidden <video> tag. Just when hidden <video> tag is ready to play (onCanPlay event), it switches the visibility of the two <video> tags. Ideally that's how the black screen will not be visible at all.
Sample source code
// ---------- Index.tsx ----------
import { useEffect, useRef, useState } from "react";
import videojs, { VideoJsPlayerOptions } from "video.js";
import "video.js/dist/video-js.css";
import { VideoDataWithOptions } from "../../@types/types";
import { socket } from "../../lib/socket";
import VideoPlayer from "./VideoPlayer";
const hlsVideoJsOptions: VideoJsPlayerOptions = {
autoplay: true,
muted: false,
controls: true,
loop: false,
sources: [
{
src: "",
type: "application/x-mpegURL",
},
],
};
const Index = () => {
const [videoData, setVideoData] = useState({} as VideoDataWithOptions);
useEffect((): any => {
socket.on("videoData", data => {
const options = {
...hlsVideoJsOptions,
sources: [
{
src: `/videos/destination/${data.path}`,
type: "application/x-mpegURL",
},
],
};
setVideoData({ ...data, options });
});
return () => socket.off("videoData");
}, []);
return <VideoPlayer videoData={videoData}></VideoPlayer>;
};
export default Index;
// ---------- VideoPlayer.tsx ----------
type Props = { videoData: VideoDataWithOptions }; // ex. => { path: string, title: string, loop: boolean, options: VideoJsPlayerOptions, ... }
const VideoPlayer = ({ videoData }: Props) => {
const videoRef = useRef<HTMLVideoElement>(null);
const playerRef = useRef<any>(null); // null
useEffect(() => {
// Make sure Video.js player is only initialized once
if (!playerRef.current) {
const videoElement = videoRef.current;
if (!videoElement) return;
playerRef.current = videojs(videoElement, videoData.options, () => {});
} else {
}
}, [videoData, videoRef]);
return (
<div data-vjs-player>
<video ref={videoRef} className="video-js" playsInline />
{/* <video ref={videoRef} className="video-js" playsInline /> */}
</div>
);
};
export default VideoPlayer;
// How should I go about this?