Camera does not turn off when publishVideo(false) is called

Hi. I’m trying to test out OpenVidu’s capabilities to understand if it suits to one of my client’s project.

In the process of testing we found it really confusing that even after turning off the video in the openvidu-call the webcam remains turned on.

I have dug into this problem and from the sources I can see that to switch off the video you call publishVideo(false). However, even though it does stop the video from being streamed, it does not stop the camera from being used, which in my opinion looks pretty weird.

Could you please clarify what is the point of this method if it does not prevent the device from being used and how this behaviour can be avoided?

Thanks.

After going deeper and looking at how publishVideo is implemented, I see that the cause of the problem is that the mediaStreamTrack is never removed, but instead it’s muted, which indeed stops the video from being streamed, but does not stop the browser from using the webcam.

Is it possible to remove/add the necessary mediaTracks on demand without reinitializing the publisher?

For remove a mediastream Check this: How to unpublish without a session

For create new ones, you just to use WebRTC API https://docs.openvidu.io/en/2.14.0/api/openvidu-browser/classes/openvidu.html#getusermedia

This is not exactly what I’m asking.

My question was about the possibility of toggling video and audio without recreating the publusher. The example with getUserMedia that you have linked explicitly calls initPublisher, which is not what I need.

The reason why I’m trying to avoid creating a new publisher is simple. Every time the old publisher is replaced with the new one there is a small disruption in the connection. Ideally, user should have an option to toggle his video/audio or screen sharing without any interruptions, and it seems that publishVideo/publishAudio were created exactly for that purpose, but at the moment they don’t work as expected.

Is it something that doesn’t bother anyone except for me or are you planning to implement something like what I described in the future?

Regards

Alright, so I’ve done some research and found out that the way you are handling the video/audio toggling is the right way according to the spec. The problem lies in the browser’s implementation of the current spec.

Firefox, for example, handles the change of mediaTrack.enabled property and switches the camera/mic when it’s set to false. But chrome and safari currently don’t do that and leave the device always on.

More about this can be found here for anyone stumbling upon this issue:

Unfortunately, from the user’s perspective it’s very awkward when the webcam light stays on along with the “on air” icon on the tab. I’ve figured that recreating the publisher and replacing it on the fly can solve this issue, but it creates a short disruption in the communication, which is not ideal.

Then I’ve tried creating two publishers one for audio and another for video, but I couldn’t get it working properly because in the current architecture this requires creating two OpenVidu instances which causes one of them to treat the other as a remote connection which causes a bunch of issues.

And lastly I’ve tried messing up the OpenVidu’s internals and manually stopping the mediaStreamTracks and doing something like stream.replaceMedia when the settings change. But with this approach to restart the stream on the remotes the peer needs to renegotiate and this is something that I don’t know how to do yet.

So I guess if anyone else would want to avoid this behaviour the best bet is to try the first approach and replace the publisher on the fly with the appropriate settings.

I would like to get some feedback on this because maybe there is a chance to implement this that I’m not aware of and then I’ll close this issue.

Hello @nsls,

I agree with you what disabling camera should turn off camera light. Have you tried with RTCRtpSender replaceTrack with null?

I’ve just tried that and that actually seems to work!

When the video is active I get the current video sender like this:
publisher.stream.getRTCPeerConnection().getSenders()[0]
and the current videoTrack like this
publisher.stream.mediaStream.getVideoTracks()[0];

Then for stopping the video I do

sender.replaceTrack(null);
videoTrack.stop();

To re-enable the video I get the new video track and call replace once again

ov
    .getUserMedia({
        audioSource: false,
        videoSource: undefined,
        resolution: '640x480',
        frameRate: 60,
    })
    .then((mediaStream) => {
        const videoTrack = mediaStream.getVideoTracks()[0];

        const sender = session.publisher.stream
        .getRTCPeerConnection()
        .getSenders()[0];

        sender.replaceTrack(videoTrack);
    });

The result is the following:
Once the local video track is stopped, the video on the remote end freezes.
After the local video track is re-created, the video on the remote end resumes with the new track.

The only problem is that it seems that no events are being dispatched when the call the replaceTrack manually, which leaves the remote user with a frozen video tag.

I guess I should use the OpenVidu’s websocket and send a message which should be handled by other users and remove/create video tags when needed.

Otherwise this seems to be the solution!

Maybe you can disable the video first like it is implemented right now (to send black) and then replaceTrack(null) to turn off the camera.

What do you think?

With a lot of struggle and pain I eventually made it to work.

In short, your solution has worked for the cases where the video was turned on initially. But if user decides to join without the video and there is no video sender available on the stream this doesn’t work since there is nothing to replace.

To solve this I ended up creating a publisher for the test room (where user chooses devices and preferences), the publisher always initializes with the video and mic on if the devices are available. If user decides to switch off the video, I manually stop the video track on the mediaStream without removing it. Keeping the stopped track is important because it allows us to replace it with a new one later on.

When user decides to join the session I make sure that videoActive property is set to true and use the existing publisher. Then if user decides to switch on the video, I use the replaceTrack on the RtcRTPSender and it shows up to all the members of the session without interruptions.

Also I’ve found one interesting article regarding the renegotiation implementation which might be useful later, when the browsers will implement it properly.

Honestly, I hope that some time soon the developers wouldn’t have to come up with this on their own and browsers will take care of it by themselves. But for now even Google Meet seems to work that way and it was the source of the inspiration for my approach.

Thank you for help, for now I’m going to close this issue.

3 Likes

Thank you for sharing your findings…

We will evaluate to implement it in the official OpenVidu Call.

@micael.gallego - this feature landed in official library?

I think not in 2.15.0 may be in near future
Thanks

Hello @micael.gallego,

I am being bitten by this issue as well. Has this feature been added ? If not when can we expect it? Is there anyway I could upvote it?

Thanks
Jai

We will take a look in the following weeks.

Thank you for reporting

Hello,

Any update on this?

Thanks
Jai

2 Likes

Hello,

Do we have any update on this. I am also facing a similar problem but unable to solve this.
I don’t want to turn on the Video, while creating the publisher. Below is the Property I am passing in OV.initPublisher method
{“frameRate”:30,“insertMode”:“APPEND”,“mirror”:false,“publishAudio”:true,“publishVideo”:false,“resolution”:“640x480”}
By Specifying publishVideo as false, the webcam is not sending the feed, I can see black image in the Video, but still the camera light gets turned on, I am unable to turn it off.

Note: This Issue is appearing only in chrome, whereas in firefox the camera light gets turned off.

I tried using below code to stop the Video Track. But if we stop the VideoTrack, it inturn affects the Audio and the subscriber is unable to hear the Audio.

setTimeout(() => {
let videoTrack;

try {

	if (publisher&& publisher.stream && publisher.stream.mediaStream
		&& publisher.stream.mediaStream.getVideoTracks()) {
			videoTrack = publisher.stream.mediaStream.getVideoTracks()[0];
	}
} catch(error) {
	console.log(error);
}
try {
	
	if (videoTrack) {
		videoTrack.stop();  
	}
}catch(error) {
	console.log(error);
}

}, 3000);
Any help would be appreciated

One thing is publishing a video track to the session. Other thing is muting that video track. videoSource property manages the presence of a video track. publishVideo manages the muted state of the video track, if present. If videoSource is initializing the Publisher object with a video track, then the light may turn on no matter the value of publishVideo. If you want an audio only Publisher, then you must initialize the Publisher object with videoSource to false. But that Publisher won’t be able to publish video later, you must take that into account.

Is there an update on this feature? Right now, I use publishVideo to turn off the camera, but still the camera indicator is still on. Is there an easier workaround for me to turn off the camera completely, and switch it back on when the user decides to turn on the camera?

If no, from what @nsls has commented above, is there a working example that I can take a look at? I am confused on why do we need to create the publisher in the test/preview room and how do we manually stop the video track without removing it?

What I have right now is something like this:

useEffect(() => {
    if (!isInMeetingRoom || !openvidu || !publisher) return

    if (!isUseVideo) {
        publisher.publishVideo(false);
        return
    }

    console.log("MeetingRoom - Replace Publisher Stream - Video")

    const currentVideoDeviceId = publisher?.stream
        ?.getMediaStream()
        ?.getVideoTracks()[0]
        ?.getSettings()
        ?.deviceId

    if (!currentVideoDeviceId) return

    if (currentVideoDeviceId === selectedVideoDeviceId) {
        publisher.publishVideo(true)
        return
    }

    openvidu.getUserMedia({
        ...defaultPublisherProperties,
        videoSource: selectedVideoDeviceId // The source of video. If undefined default webcam
    }).then(mediaStream => {
        console.log(`MeetingRoom - Replace Publisher Stream - Video ${mediaStream.id}`)
        const newVideoTrack = mediaStream.getVideoTracks()[0]
        publisher.replaceTrack(newVideoTrack)
            .then(() => {
                console.log("MeetingRoom - Replace Publisher Stream - Video - Success")
                publisher.publishVideo(true)
                stopStreamTracks(publisherVideoStream)
                dispatch(setPublisherVideoStream(mediaStream))
            })
            .catch(error => {
                console.log("MeetingRoom - Error Replace Publisher Stream - Video", error)
            })
    }).catch(error => {
        console.log("MeetingRoom - Error Replace Publisher Stream - getUserMedia Video", error)
    })
}, [isInMeetingRoom, openvidu, publisher, isUseVideo, selectedVideoDeviceId])

From the code above, isUseVideo is basically the state of whether the camera should be turned on or not. And note that, I only initialized openvidu when user gets into the meeting room.

Any help would be extremely appreciated.

What version are you using? I think this was fixed at some point or there is a flag to disable the camera. @pabloFuente am I right? :thinking:

I’m currently using "openvidu-browser": "^2.20.0".

Additional note: This buggy behavior only happens in Chrome. In Firefox, the video gets switched off and the camera indicator also went off.