3

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?

Michael M.
  • 10,486
  • 9
  • 18
  • 34
Maurício Giordano
  • 3,116
  • 4
  • 32
  • 60

2 Answers2

1

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>
VC.One
  • 14,790
  • 4
  • 25
  • 57
Velexior
  • 169
  • 2
  • 8
  • You need to fix your Answer. The first point has no proof. Why would constant looping help, if that's not a wanted feature? Why can't it pause on ending and wait for user to choose when to seek in timeline or when to play? Note: the `ended` event cannot be cancelled or prevented, it will auto-destroy the stream when video's ending has been detected... How to avoid the auto-destroy? That is the question and the solution is to get it to pause on last frame and still be active. – VC.One Jan 29 '23 at 20:09
  • PS: I recommend you create a demo with two video tags, stream one to the other and show testable code as proof of solution. Your Answer has the closest chance of winning the bounty (but not guaranteed without proof). Also your Answer cannot be upvoted as it is currently written. Example: Where is `pc` and `leftVideo` coming from? They are undeclared (and unexplained) variables. Fix this if possible. – VC.One Jan 29 '23 at 20:14
  • 1
    @VC.One I pasted a link to the github repo with a demo – Velexior Jan 30 '23 at 11:38
  • The person who started the bounty isn't responding. I cannot award a bounty of my own to your Answer. It says the answer must be _"exemplary or outstanding"_, which won't happen with this type of question. The loop/pause solution is some options the Asker should have tried before doing a bounty. – VC.One Feb 03 '23 at 22:03
-1

Maybe make a mixin and save the status of this variable in a store.

  • The object is still the same. – Maurício Giordano Mar 24 '21 at 21:32
  • 2
    HI @JoaoAugusto Welcome to Stack Overflow. The OP has commented on your answer, and it looks like they are saying it didn't answer their question. Can you please make your answer more useful by including a code snippet - like to a reference that shows how the OP can be solved. – Mr R Mar 30 '21 at 10:41