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

2025-01-29  本文已影响0人  wn777

WebRTC

什么是WebRTC

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

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

特点

谁在用

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

各大语音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,可以满足不同环境,以及网络情况下的端到端通信 ,则我们需要构建

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

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

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 = 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,
        },
    ],
});

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

// 收到offer
// 存储offer在本地
called.setRemoteDescription(event.offer)
// 创建answer
answer = await called.createAnswer();
// 存储answer在本地
await called.setLocalDescription(answer);

await pc.setRemoteDescription(event.answer);

// 收到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连通网络后,便可以推流到对端。

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

笔记本视角:

手机端视角:

注意:

附录

代码

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

【Your WebRTC】

名词解释

ICE 候选者 (Candidate)

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

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 是一种格式化的文本,用于描述多媒体会话的参数。它包含的信息包括:

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

上一篇 下一篇

猜你喜欢

热点阅读