使用 WebRTC 和 React 从画布流式传输视频
使用WebRTC,您可以从canvas直播视频。本文提供了一个分步指南,向您展示如何实现。我们使用LiveKit的WebRTC堆栈来构建一个用于发送canvas视频的实时应用程序。查看完整代码。

许多人知道WebRTC是支持网页视频聊天的技术。这对于像Google Meet这样的应用非常有用,但它可用于构建的远不止简单的电话会议。
事实上,您可以在canvas上放置的任何内容(种类繁多!)都可以作为视频发送到WebRTC会话中。
在我们深入细节之前,请注意这里介绍的技术通常适用于任何堆栈。我们使用LiveKit来简化发布流程。请遵循此迷你教程,了解如何设置您自己的LiveKit项目。
使用 captureStream() 捕获 Canvas
WebRTC基于MediaStreams的概念,它就是……呃……媒体流。为了将canvas发送到会话中,我们需要从canvas获取一个MediaStream。幸运的是,使用HTMLCanvasElement.captureStream() API可以非常轻松地实现。
让我们创建一个简单的canvas,在上面画一个圆圈,然后从中创建一个MediaStream
export const CircleCanvasPublisher() {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const mediaStreamRef = useRef<MediaStream | null>(null);
// Draw a circle
useEffect(() => {
if(!canvasRef.current) return;
const ctx = canvasRef.current.getContext("2d");
ctx.beginPath();
ctx.arc(canvasRef.current.width / 2, canvasRef.current.height / 2, 50, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}, []);
// Create MediaStream
useEffect(() => {
if(mediaStreamRef.current || !canvasRef.current) return;
// Create 30fps MediaStream
mediaStreamRef.current = canvasRef.current.captureStream(30);
}, [])
return (
<canvas width={300} height={300} ref={canvasRef} />
)
}在上面的代码片段中,我们
- 创建了一个canvas并存储了对它的引用
- 在canvas上画了一个小圆圈(何乐而不为?)
- 以每秒30帧的速度从canvas创建了一个
MediaStream并存储了对它的引用
现在我们有了MediaStream。剩下的就是将其发布到您的会话中。使用LiveKit,这只需要几行代码
// At the top of the component
const { localParticipant } = useLocalParticipant()
// In the useEffect that creates the MediaStream
const publishedTrack = mediaStreamRef.current.getVideoTracks()[0]
localParticipant.publishTrack(publishedTrack, {
name: "video_from_canvas",
source: Track.Source.Unknown
})将它们组合在一起
瞧!将canvas作为视频发送就是这么简单。我们在 WebGL canvas 上使用了相同的技术来制作我们的 VTuber 流媒体应用
这是包含使用LiveKit创建会话的附加代码的完整代码
import { useEffect, useRef, useCallback } from "react";
import { Track } from "livekit-client";
import { useLocalParticipant } from "@livekit/components-react";
export const CircleCanvasPublisher() {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const mediaStreamRef = useRef<MediaStream | null>(null);
const { localParticipant } = useLocalParticipant()
// Draw a circle
useEffect(() => {
if(!canvasRef.current) return;
const ctx = canvasRef.current.getContext("2d");
ctx.beginPath();
ctx.arc(canvasRef.current.width / 2, canvasRef.current.height / 2, 50, 0, 2 * Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
}, []);
// Create MediaStream
useEffect(() => {
if(mediaStreamRef.current || !canvasRef.current) return;
// Create 30fps MediaStream
mediaStreamRef.current = canvasRef.current.captureStream(30);
// Publish the canvas video
const publishedTrack = mediaStreamRef.current.getVideoTracks()[0]
localParticipant.publishTrack(publishedTrack, {
name: "video_from_canvas",
source: Track.Source.Camera
})
}, [])
return (
<canvas width={300} height={300} ref={canvasRef} />
)
}@/components/CircleCanvasPublisher.tsx
import { LiveKitRoom } from "@livekit/components-react";
import { CircleCanvasPublisher } from "@/components/CircleCanvasPublisher";
type RoomProps {
token: string
wsUrl: string
}
export const Room = ({token, wsUrl}: RoomProps) => {
return (
<LiveKitRoom token={token} serverUrl={wsUrl} connect={true}>
<CircleCanvasPublisher />
</LiveKitRoom>
)
}@/components/Room.tsx
加入我们
您现在应该拥有在WebRTC会话中发布canvas媒体所需的一切。如果您对此帖子有任何反馈,或者最终使用或受其启发构建了很酷的东西,请加入LiveKit Slack社区告诉我们。我们很高兴听到大家的声音!