Possible to put video in video element instead of canvas

Video SDK Type and Version
1.10.7

Description
Is it possible to show remote videos in their own video element instead of on a canvas? I want to do this for layout and UI purposes. I want to mask the video inside of a circle or rectangle but I do not know the absolute values in pixels of the mask size so I cannot use the maskoption in the stream.

Hi @tylertyler,

Thank you for your post!

With version 1.10.7, developers can use the attachVideo function. The attachVideo function allows developers to render videos as individual elements, rather than on a canvas. Please follow the “Individual HTML Elements” section of the Start Video documentation to achieve what you are looking for.

Let me know if I can be of any further assistance.

Best,
Will

I must be doing something wrong then because I’m using attachVideo, I pass it a VideoPlayer element and the remote video is still being rendered in a canvas.

Can you see any issues here?

My code (react):

<video-player-container>
    <video-player ref={videoPlayerRef} />
</video-player-container>
...
await zoomClient.getMediaStream().attachVideo(userId, VideoQuality.Video_720P, videoPlayerRef.current);

Output on the page:

<video-player-container>
    #shadow-root
    <canvas /> // this is where the video display is
</video-player-container>

Hi @tylertyler,

A few asks:

  1. Could you share steps to reproduce?
  2. Can you share a screenshot of the DOM itself?

Thanks,
Will

Here are stripped down components that deal with the videos for my app:

interface ParticipantListProps {
	currentParticipantList: number[]
}

const ParticipantList: FunctionalComponent<Props> = ({
	currentParticipantList
}: ParticipantListProps) => {
	const buildParticipantList = () => {

		return currentParticipantList.map((identity: number) => {
			return <Participant key={identity} participantId={identity} />
		})
	};

	const participants = useMemo(buildParticipantList, [
		currentParticipantList,
	]);

	return (
		<video-player-container>
			{participants}
		</video-player-container>
	);
};

interface ParticipantProps {
	currentParticipantList: number[]
}

const Participant: FunctionalComponent<ParticipantProps> = ({
	identity
}: ParticipantProps) => {

	return (
		<div>
			<div>
				<Track key={participantId} userId={participantId} />
			</div>
		</div>
	);
};

interface TrackProps {
	userId: number;
}


const Track: FunctionalComponent<TrackProps> = ({
	userId,
}: TrackProps) => {
	const videoPlayerRef = useRef();

	useEffect(() => {
		if (videoPlayerRef.current) {
			attachVideoStream();
		}

		return detachVideoStream;
	}, [videoPlayerRef]);

	async function attachVideoStream() {
		try {
			const user = await zoomClient.getUser(userId);
			if (user && user.bVideoOn) {
				await zoomClient
					.getMediaStream()
					.attachVideo(
						userId,
						VideoQuality.Video_720P,
						videoPlayerRef.current,
					);
			} else {
				console.log('user is not sharing');
			}
		} catch (e) {
			console.log('error attaching video stream', e);
		}
	}

	async function detachVideoStream() {
		try {
			await zoomClient
				.getMediaStream()
				.detachVideo(userId, videoPlayerRef.current);
		} catch (e) {
			console.log('error detaching video stream', e);
		}
	}

	return (
		<div>
			<video-player class={`${style.video} video`} ref={videoPlayerRef} />
		</div>
	);
};

To reproduce this I create a <ParticipantList /> and pass it a prop with a list of identities that are in the zoom room. That list will create a <Participant /> for each identity which in turn will create a <Track /> for each participant. In this Track component I use the attachVideo method, passing it the ref to the <video-player> element.

Below is a screenshot of what happens with one remote participant in the zoom room.

Hey @tylertyler

Yes, indeed, our underlying implementation renders video using canvas. However, you can still use the video-player element for CSS styling purposes.

May I understand if you are looking to implement a masking feature?

I’m glad to hear that you’ve explored our mask feature. In the Video SDK Web, the masking functionality is only applicable to self-view video. This means that the mask is applied to the captured video stream before sending it to remote participants. Therefore, the received video stream on the remote end is already processed.

I want to mask the video inside of a circle or rectangle

My recommendation would be to create a div element at the same level as the video-player, overlaying it. Then, you can achieve the masking functionality by modifying the properties of this div element, such as border, background, etc.

I hope this helps. If you have any further questions, please feel free to reach out to me.

Thanks
Vic

I don’t think that’s necessarily true.

Here I wrapped the <video-player> in a div that has a set height and width - that works - but the displayed video does not respect the overflow: hidden property. You can see this container div has a border-radius of 50% (that is seen in the display) but the video is displayed outside the boundaries of this div where if what you said is true then I would expect the video to not be overflowing from the div.

Hey @tylertyler

I understand your point.

In reality, the video is rendered on the underlying canvas, and the video-player element serves primarily to assist in positioning the video coordinates. Consequently, videos are rendered in a square manner and cannot have rounded corners

This limitation stems from the current implementation. If employing the method I suggested earlier can resolve your issue in certain scenarios, then that would be an optimal solution.

Thanks
Vic

This unfortunately won’t work to mask and overlay it on top of other content. Setting border and background would not mask the content, it would just overlay something over top of the video right?

Hey @rg11389

Yes. It simply overlays the video with additional elements.

Thanks
Vic

Okay thank you @vic.yang for the information.

It sounds like at this point the Video SDK is not fully customizable for any UI needs and we will wait on updated versions hoping these things get added