The trick is to have a container div that is what holds your video tag (as a child object). When there is an error, you delete the child object (video tag) and dynamically create a new image tag, that gets added as a replacement child into the container.
The result is: (within container div…) Delete the video tag and Replace with an image tag.
The user sees the video object replaced by an image.
See if this example code helps you to solve your problem:
<!DOCTYPE html>
<html>
<body>
<!-- should make the div's width & height to be same as video's display size -->
<!-- example code is assuming video is 600x400 -->
<div id="container_vid1" style="width:600px;" />
<video
id="vid"
controls
autoplay
controlsList="nodownload"
preload="true"
playsinline=""
muted
width="600"
height="400"
>
<source
src="https://xxx.s3.eu-central-1.amazonaws.com/yyy/zzz.mp4"
type="video/mp4"
/>
</video>
</div>
<script>
var path_to_img = "https://outbackpoolgp.com/wp-content/uploads/2016/11/640x400-water-mobile-3.jpg"; //# update this with image path
var vid = document.getElementById("vid"); //reference the video tag
vid.addEventListener( "error", (evt) => { handle_VideoError(evt); }, true);
function handle_VideoError(evt)
{
alert(
"Error from <video> element : " + evt.currentTarget.id
+ "n" //# looks better on new line
+ "File not found : " + evt.currentTarget.currentSrc
);
//# access the parent div
let container = document.getElementById("container_vid1");
//# in div... remove any existing child < element(s) />
while (container.firstChild)
{ container.removeChild(container.firstChild); }
//# create (& add to div) a new <img> element
let tmpElement = document.createElement( "img");
tmpElement.setAttribute("id", "vid"); //# set same id as previous video tag (if preferred)
tmpElement.setAttribute("width", container.clientWidth );
tmpElement.setAttribute("src", path_to_img );
container.appendChild( tmpElement );
}
</script>
</body>