美文网首页
【WebRTC - OpenAI 都在用? 实战秘籍,10分钟!

【WebRTC - OpenAI 都在用? 实战秘籍,10分钟!

作者: wn777 | 来源:发表于2025-01-29 01:30 被阅读0次

WebRTC

什么是WebRTC

WebRTC(Web Real-Time Communication)是一项由 W3C 和 IETF 推动的开源项目,旨在为浏览器和移动应用提供实时通信(RTC)功能。

WebRTC 支持音频、视频和数据在点对点(P2P)连接中直接传输,而无需中间服务器。也支持配合SFU(Selective Forwarding Unit),MCU(Multipoint Control Unit)技术,构建多对多的传输通信。

特点

  • 实时通信: 支持音频、视频和数据的实时传输,延迟低,适用于实时互动场景。
  • 点对点连接: 通过 P2P 连接,数据直接在客户端之间传输,减少了服务器的负载和延迟。
  • 跨平台支持: 支持主流浏览器(如 Chrome、Firefox、Safari、Edge)和移动平台(如 Android、iOS)。
  • 安全性: 默认使用 SRTP(Secure Real-time Transport Protocol)加密音视频流,DTLS(Datagram Transport Layer Security)加密数据通道,确保通信安全。
  • 开源和标准化: WebRTC 是一个开源项目,并且由 W3C 和 IETF 标准化,确保广泛的兼容性和支持。

谁在用

可以看出 基于 网络语音/视频通话 的场景,尤其是类似 实时网络 语音电话这种。

各大语音app (whats app, Facebook, Google系软件) 都有基于webrtc或者参考webrtc的思路进行实现。refs: https://telnyx.com/resources/5-applications-that-demonstrate-the-power-of-webrtc-and-sip

OPENAPI 也推出了实时流视频接口,Realtime API with WebRTC https://platform.openai.com/docs/guides/realtime-websocket

快速构建

速览图

一图速览,可以看出 建立WebRTC的通信,整体分 建立网络连接 和 推流 两大步。

而网络连接 又分P2P模式 和 中继模式。

构建一个完整的WebRTC,可以满足不同环境,以及网络情况下的端到端通信 ,则我们需要构建

  • 信令服务
  • STUN服务
  • TURN服务

此外演示的代码跑在浏览器上,访问浏览器展示页面,创建WebRTC Client,所以还需要一个Web服务

  • Web HTTP服务

废话不多说,逐一部署,一个个击破。接下来,上家伙 (完整代码见附录)

1. 部署 Web HTTP 服务 + Signaling 信令服务

构建一个Web Http 服务器,可以返回 前端页面所需数据,

...
const server = http.createServer((req, res) => {
    console.log(`request url: ${req.url}, method: ${req.method}`);

    // 提供静态文件服务
    // 返回主页
    if (req.method === "GET" && req.url === "/") {
        console.log("request index.html");
        const filePath = path.join(
            __dirname,
            "static/my-webrtc-client/index.html"
        );
        fs.readFile(filePath, (err, data) => {
            if (err) {
                res.writeHead(404, { "Content-Type": "text/plain" });
                res.end("404 Not Found");
                return;
            }
            // 根据文件扩展名设置正确的 Content-Type
            const ext = path.extname(filePath).toLowerCase();
            const contentType = mimeTypes[ext] || "application/octet-stream";
            res.writeHead(200, { "Content-Type": contentType });
            res.end(data);
        });
    } else if (req.method === "GET" && isRequestFile(req)) {
    // 返回 js / css 等文件
        const dirpath = __dirname + "/static/my-webrtc-client";
        const filePath = path.join(dirpath, req.url);
        console.log(`request ${filePath}`);

        fs.readFile(filePath, (err, data) => {
            if (err) {
                res.writeHead(404, { "Content-Type": "text/plain" });
                res.end("404 Not Found");
                return;
            }

            // 根据文件扩展名设置正确的 Content-Type
            const ext = path.extname(filePath).toLowerCase();
            const contentType = mimeTypes[ext] || "application/octet-stream";
            res.writeHead(200, { "Content-Type": contentType });
            res.end(data);
        });
    } else {
        res.writeHead(404, { "Content-Type": "text/plain" });
        res.end("404 Not Found");
    }
});
...

构建一个Signaling server,用于两端数据的交换。

io.on("connection", (sock) => {
    console.log("连接成功...");
    // 向客户端发送连接成功的消息
    sock.emit("connectionSuccess");

    // 监听客户端event
    sock.on("offer", (event) => {
        console.log(`receive offer from device : ${event.fromDeviceId}`);
        // 向其余客户端发送offer
        sock.broadcast.emit("offer", event);
    });

    sock.on("candidate", (event) => {
        console.log(`receive candidate from device : ${event.fromDeviceId}`);
        // 向其余客户端发送offer
        sock.broadcast.emit("candidate", event);
    });

    sock.on("answer", (event) => {
        console.log(
            `receive answer from deviceId : ${event.fromDeviceId}, to deviceId : ${event.toDeviceId}`
        );
        // 向其余客户端发送offer
        sock.broadcast.emit("answer", event);
    });

    ...
});

2. 部署 STUN服务 + TURN服务

此处选用coturn服务,部署安装

安装

apt install coturn

修改coturn config, 参考 【WebRTC - STUN/TURN服务 - COTURN配置】

vim /etc/turnserver.conf

启动coturn服务

turnserver --log-file stdout

3. 使用 WebRTC Client进行连接

构建webrtc client , webrtc场景下, 端到端通信,A为 Caller,B为 Called。WebRTC Client A → WebRTC Client B 。

  • Caller 和 Called 两端分别初始化
caller / called = new RTCPeerConnection({
    encodedInsertableStreams: true, // needed by chrome shim
    iceServers: [
        {
            // default "stun:stun.l.google.com:19302",
            urls: STUN_URL,
        },
        {
            urls: TURN_URL,
            username: TURN_USERNAME,
            credential: TURN_CREDENTIAL,
        },
    ],
});

  • Caller 请求对端 ,创建offer然后存储在本地,然后发送offer
// 创建offer
const offer = await pc.createOffer(offerOptions);
// 存储在本地
caller.setLocalDescription(offer)
// 发送到对端
sock.emit("offer", {
    offer: offer,
    ...
});

  • Called接收到offer,存储在本地,然后创建answer 然后回复answer给Caller
// 收到offer
// 存储offer在本地
called.setRemoteDescription(event.offer)
// 创建answer
answer = await called.createAnswer();
// 存储answer在本地
await called.setLocalDescription(answer);

  • Caller在收到answer以后,存储在本地
await pc.setRemoteDescription(event.answer);

  • 同时 在无论是Caller 还是 Called 在调用setLocalDescription后,会触发浏览器请求STUN服务器获取candidate信息,Caler / Called 需要获取到candidate 然后存在本地
// 收到candidate
caller/called.addEventListener("icecandidate", (e) => {
    // 发给对端
    sock.emit("candidate", {
        candidate: candidate,
        ...
    });
})

// 对端收到后 ,存储在本地
await caller/called.addIceCandidate(candidate);

当两端互换candidate 协商后,根据candidate的type (host, relay 等) 尝试连通,最终决定是p2p通信,还是 relay通信。至此便完成了两端网络连通。

当网络连通后,Caller获取本地音视频数据后 ,用刚刚打通的网络,便可进行数据传输。Called接收数据。

Caller

// 获取 音视频数据
stream = await navigator.mediaDevices.getUserMedia({
    audio: true,
    video: true,
});
// 传输
stream.getTracks().forEach((track) => {
    caller.addTrack(track, stream);
});

Called

// 收到后, 将数据放到 video element上,进行播放
called.addEventListener("track", (e) => {
    const remoteVideo = document.getElementById("remoteVideo");
    remoteVideo.srcObject = e.streams[0];
});

WebRTC - 展示

p2p 模式:两个设备都在同一个局域网内 ,

打开两个网页端,分别是Caller 和 Called。
Caller 点击 Start 获取媒体流,然后点击Call 和 对端Called连通网络后,便可以推流到对端。

中继模式: 两个移动端设备在公网环境 ,一个笔记本电脑 ,一个手机

笔记本视角:

手机端视角:

注意:

  • 如果两端不在同一局域网内,大概率是需要中继服务的,则此时,信令服务,TURN/STUN服务需要部署在有公网地址的机器上。
  • STUN有免费公共的可以使用,但是TURN由于带宽费用高,没有免费公共的,所以必须要自部署。

附录

代码

示例代码,包括 前后端代码,信令服务 。

【Your WebRTC】

名词解释

ICE 候选者 (Candidate)

ICE 候选者包含关于如何连接到对等方的网络信息。每个候选者提供一个可能的网络路径。具体信息包括:

  • IP 地址: 设备的公共或私有 IP 地址。
  • 端口: 用于通信的端口号。
  • 优先级: 候选者的优先级,用于选择最佳路径。
  • 类型: 候选者的类型(如 host、srflx、relay)。
  • 协议: 使用的传输协议(如 UDP、TCP)。

candidate 示例

candidate:842163049 1 udp 1677729535 192.168.1.2 56143 typ srflx raddr 10.0.0.1 rport 8998 generation 0 ufrag EEtu network-id 3 network-cost 10

SDP (Session Description Protocol)

SDP 是一种格式化的文本,用于描述多媒体会话的参数。它包含的信息包括:

  • 会话描述: 包括会话的名称、时间、参与者等。
  • 媒体描述: 包括媒体类型(音频、视频)、编解码器、比特率等。
  • 连接信息: 包括 IP 地址和端口号。
  • 媒体格式: 支持的媒体格式和编解码器。
  • 带宽信息: 会话的带宽要求。
  • 属性: 其他会话属性,如方向(发送、接收)。

sdp 示例

v=0
o=- 46117327 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104
c=IN IP4 192.168.1.2
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:EEtu
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=mid:audio
a=sendrecv
a=rtpmap:111 opus/48000/2
m=video 9 UDP/TLS/RTP/SAVPF 100 101
c=IN IP4 192.168.1.2
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:EEtu
a=ice-pwd:asd88fgpdd777uzjYhagZg
a=mid:video
a=sendrecv
a=rtpmap:100 VP8/90000

相关文章

网友评论

      本文标题:【WebRTC - OpenAI 都在用? 实战秘籍,10分钟!

      本文链接:https://www.haomeiwen.com/subject/ybysujtx.html