Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

video.captureStream stops when video is over

I'm building a custom WebRTC solution that allows you to use an mp4 file as if it was your camera.

To do that, I'm creating a <video> element, using captureStream() to generate a MediaStream and transmitting that to our servers. It works great, but if the user seeks the video to the end, the MediaStream gets inactive and I can't revert it back to active without using captureStream() again. Sometimes the user may want to play something back again and the video element works, but the MediaStream is inactive, so I don't receive any content on my server.

Here is the code:

this.video = document.createElement("video");
this.video.src = url;
this.mediaStream = this.video.captureStream(25);

I want to keep the MediaStream active waiting for content even if the video is over in case the user wants to play it again. How can I do this?

like image 865
Maurício Giordano Avatar asked Nov 17 '25 21:11

Maurício Giordano


1 Answers

I found two ways to solve this issue.

First and easier way is to loop your video, either add "loop" in your video element in HTML file, or through JavaScript.

If for some reason you don't want to loop your video (e.g. not a wanted behavior), you can do something like this:

video.addEventListener('play', (event) => {
  // check if your media stream is active
  // (user can pause/unpause so we need to do a check)
  if (!mediaStream.active) {
    // user clicked play on a video that finished playing
    // we want to get a new stream and replace a videoTrack in the existing and established RTCPeerConnection
    const newstream = video.captureStream();
    const sender = pc.getSenders().find((s) => s.track.kind === "video");
    sender.replaceTrack(newstream.getVideoTracks()[0]);
  }
});

Edit 1: I made a github repo to demo the solution: https://github.com/mluketin/webrtc-video-to-pc

Edit 2: I fixed 'leftVideo' variable that should have been named 'video'

Edit 3:
Another way to avoid using loop is to pause() the video on ending. You cannot wait until vid.currentTime == vid.duration as that closes the stream. However currentTime does fire at every frame so it can never precisely stop at last frame (workarounds include using video's own FPS or vid.requestVideoFrameCallback() to know how many more frames (milliseconds) to advance forward by, but this per-frame advancing is beyond the scope of this question).

Here is a starting example code:
note: The number 0.225 should be actually 0.333 but it seems not precise enough to avoid triggering an onEnded event. The 0.333 would be from (1000 / FPS) / 100 for example (1000/30FPS) / 100) = 0.333.

<!DOCTYPE html>
<html>
<body>

<div>
<video id="vid_sender" width="320" height="240" controls>
<source src="movie.mp4" type="video/mp4">
</video>
<b> Sender... </b>
</div>

<div>
<video id="vid_getter" width="320" height="240" >
<source src="" type="video/mp4">
</video>
<b> Receiver... </b>
</div>

</body>

<script>

var vid_sender = document.getElementById("vid_sender");
var vid_getter = document.getElementById("vid_getter");

var vid_FPS = 30; //# use actual video FPS here
const stream = vid_sender.captureStream( vid_FPS );

//# un-comment this line to see problem as solved
//# solution is to pause the video before it ends
vid_sender.ontimeupdate = (evt) => myFunction (evt);

vid_getter.srcObject = stream;
vid_getter.load(); vid_getter.play();

function myFunction (input) 
{
    if( input.target.currentTime >= (input.target.duration - 0.250) )
    { 
        input.target.pause();
        alert("time stop: " + input.target.currentTime + " of duration: " + input.target.duration);
    }
}

</script>

</html>
like image 180
Velexior Avatar answered Nov 19 '25 12:11

Velexior



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!