webRtc 示例

1、使用socket.io进行会话

2、为了方便,参数写死在前端了,前端界面1代码如下(由界面1发起视频):

html 复制代码
<!DOCTYPE html>
<html>

<head>
    <title>Socket.IO chat</title>

    <meta charset="utf-8">
    <meta name="description" content="WebRTC code samples">
    <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
    <meta itemprop="description" content="Client-side WebRTC code samples">
    <meta itemprop="name" content="WebRTC code samples">
    <meta name="mobile-web-app-capable" content="yes">
    <meta id="theme-color" name="theme-color" content="#ffffff">

    <!-- 引入2.0版本的socket.io文件 -->
    <script src="socket.io.js"></script>
    <script src="webrtc.js"></script>
    <script src="jquery-3.6.4.js"></script>

    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="css/main.css" />
    <link rel="stylesheet" href="css/c1/main.css" />

</head>

<body>

    <div id="container">

        <video id="localVideo" title="a" playsinline autoplay muted></video>
        <video id="remoteVideo" title="b" playsinline autoplay></video>

        <div>
            <button id="prepareBtn" type="button" onclick="prepare()">准备</button>
            <button id="callBtn" type="button" onclick="callVideo()">呼叫</button>
            <button id="cancelBtn" type="button" onclick="cancelVideo()">挂断</button>
        </div>

    </div>
    <script>
        const prepareButton = document.getElementById('prepareBtn');
        const callBtn = document.getElementById('callBtn');
        const cancelBtn = document.getElementById('cancelBtn');

        const localVideo = document.getElementById('localVideo');
        const remoteVideo = document.getElementById('remoteVideo');

        let localStream;
        let pc1;
        let pc2;
        let to = '2';

        var iceServer = {
           
                {
                    // urls: 'stun:stun.l.google.com:19302'
                    urls: 'stun:stun.services.mozilla.com'
                }
            ]
        };


        const offerOptions = {
            offerToReceiveAudio: 1,
            offerToReceiveVideo: 0
        };


        //建立socket连接
        const socket = io("https://192.168.3.21", {
            reconnectionDelayMax: 10000,
            auth: {
                token: "123"
            },
            query: {
                "userId": "1"
            }
        });

        socket.on("connect", () => {
            console.log("通道已建立")
        });

        //监听服务端发送的 topic1 消息
        socket.on("exchange", (msg) => {
            console.log("收到新的数据了");

            var data = JSON.parse(msg)
            console.log(data);
            var type = data.type
            if (type === 'invite') {
                to = data.from
                
            } else if (type === 'recive') {
                to = data.from
            } else if (type === 'canditate') {
                if (data.data != undefined) {
                    pc1.addIceCandidate(new RTCIceCandidate(data.data))
                }
            } else if (type === 'offer') {
                //设置远程应答
                pc1.setRemoteDescription(new RTCSessionDescription(data.data))
                pc1.createAnswer().then(function (answer) {
                    send({
                        type: 'answer',
                        data: answer
                    })
                    return pc1.setLocalDescription(answer)
                }).then(sdp => {

                })
            } else if (type === 'answer') {
                if (data.data != undefined) {
                    pc1.setRemoteDescription(new RTCSessionDescription(data.data))
                }
            }
        });


        function gotStream(stream) {
            localVideo.srcObject = stream;
            localStream = stream;
        }

        async function init() {
            navigator.mediaDevices
                .getUserMedia({
                    audio: true,
                    video: false
                })
                .then(gotStream)
                .catch(e => alert(`getUserMedia() error: ${e.name}`));

        }

        async function prepare() {
            init();

            var data = {
                type: 'invite',
                data: 'video',
                from: '1',
                toUserId: '2'
            };
            // send(data);
            prepareButton.disabled = true;
        }

        function call() {

            pc1 = new RTCPeerConnection(iceServer);
            pc1.onicecandidate = e => onIceCandidate(pc1, e);

            // 有远程视频流时,显示远程视频流 
            pc1.addEventListener('track', event => {
                // remoteVideo.srcObject = event.streams[0]
                // remoteVideo.onloadedmetadata = function (e) {
                //     remoteVideo.play()
                // }
            })
            pc1.addEventListener('addstream', e => {
                console.log("AAAAAAAAAAA")
                console.log(e);
                remoteVideo.srcObject = e.stream
            })


            //初始化本人的视频流
            navigator.mediaDevices.getUserMedia({
                audio: false,
                video: true
            }).then(stream => {

                // 将视频流写入到video标签
                localVideo.srcObject = stream;
                localStream = stream;
                stream.getTracks().forEach(track => {
                    pc1.addTrack(track, stream)
                });

                //向对方发送应答
                pc1.createOffer().then(sdp => {
                    pc1.setLocalDescription(sdp)
                    send({
                        type: 'offer',
                        data: sdp
                    })
                })
            })
            localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));


            callBtn.disabled = true;
        }


        function onIceCandidate(pc, event) {
            if (event.candidate) {
                send({
                    type: 'canditate',
                    data: event.candidate
                })
            }
        }

        function send(data) {
            data.toUserId = data.toUserId || to
            console.log(data)
            socket.emit("exchange", JSON.stringify(data))
        }

        function callVideo() {
            call();
        }

        function cancelVideo() {
            if (pc1 != null && pc1 != undefined) {
                pc1.close();
            }
        }
    </script>
    <style>
        #video {
            background-color: black;
            height: 30vh;
        }
    </style>
</body>

</html>

3、前端界面2代码如下(当界面1发起视频呼叫后,该界面被动应答):

html 复制代码
<!DOCTYPE html>
<html lang="zh">

<head>
    <title>Socket.IO chat</title>
    <meta charset="utf-8">
    <meta name="description" content="WebRTC code samples">
    <meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
    <meta itemprop="description" content="Client-side WebRTC code samples">
    <meta itemprop="name" content="WebRTC code samples">
    <meta name="mobile-web-app-capable" content="yes">
    <meta id="theme-color" name="theme-color" content="#ffffff">
    <!-- 引入2.0版本的socket.io文件 -->
    <script src="socket.io.js"></script>
    <script src="webrtc.js"></script>
    <script src="jquery-3.6.4.js"></script>


    <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
    <link rel="stylesheet" href="css/main.css" />
    <link rel="stylesheet" href="css/c1/main.css" />
</head>

<body>
    <div id="container">

        <video id="localVideo" title="a" playsinline autoplay muted></video>
        <video id="remoteVideo" title="b" playsinline autoplay></video>

    </div>
    <script>
        const localVideo = document.getElementById('localVideo');
        const remoteVideo = document.getElementById('remoteVideo');
        let localStream;
        let pc1;
        let pc2;
        let to = '1';

        var iceServer = {
           
                {
                    // urls: 'stun:stun.l.google.com:19302'
                    urls: 'stun:stun.services.mozilla.com'
                }
            ]
        };

        //建立socket连接
        const socket = io("https://192.168.3.21", {
            reconnectionDelayMax: 10000,
            auth: {
                token: "123"
            },
            query: {
                "userId": "2"
            }
        });

        socket.on("connect", () => {
            console.log("通道已建立")
        });

        //监听服务端发送的 topic1 消息
        socket.on("exchange", (msg) => {
            console.log("收到新的数据了");

            var data = JSON.parse(msg)
            var type = data.type
            console.log({
                "name": "接收方",
                data
            });


            if (type === 'invite') {
                to = data.from
                //打开自己的摄像头
                init();

            } else if (type === 'recive') {
                to = data.from
            } else if (type === 'canditate') {
                if (pc1 == null) {
                    init();

                }
                if (data.data != undefined) {
                    pc1.addIceCandidate(new RTCIceCandidate(data.data))
                }
            } else if (type === 'offer') {
                if (pc1 == null) {
                    init();
                }
                pc1.setRemoteDescription(new RTCSessionDescription(data.data))
                pc1.createAnswer().then(function (answer) {
                    send({
                        type: 'answer',
                        data: answer
                    })
                    return pc1.setLocalDescription(answer)
                }).then(sdp => {

                })
            } else if (type === 'answer') {
                if (data.data != undefined) {
                    pc1.setRemoteDescription(new RTCSessionDescription(data.data))
                }
            }
        });


        function gotStream(stream) {
            localVideo.srcObject = stream;
            localStream = stream;
        }

        function init() {
            navigator.mediaDevices
                .getUserMedia({
                    audio: true,
                    video: false
                })
                .then(gotStream)
                .catch(e => alert(`getUserMedia() error: ${e.name}`));


            call();

        }


        function onIceCandidate(pc, event) {
            if (event.candidate) {
                send({
                    type: 'canditate',
                    data: event.candidate
                })

            }
        }

        function call() {

            pc1 = new RTCPeerConnection(iceServer);

            //获取本地网络信息,并发送给通信方 
            pc1.onicecandidate = e => onIceCandidate(pc1, e);

            // 有远程视频流时,显示远程视频流 
            // pc1.addEventListener('track', event => {
            //     console.log("BBBBBBBBBBBBBBBBBBBBBBBBB")
            //     console.log(event);
            //     console.log(localVideo.srcObject)
            //     console.log("BBBBBBBBBBBBBBBBBBBBBBBBB+")

            //     remoteVideo.srcObject = event.streams[0]
            //     remoteVideo.onloadedmetadata = function (e) {
            //         console.log("加载完毕")
            //         remoteVideo.play()
            //     }
            // })

            pc1.addEventListener('addstream', e => {
                // 将对方的视频流写入到video标签中
                remoteVideo.srcObject = e.stream
            })

            //本地视频流量,打开视频流
            navigator.mediaDevices.getUserMedia({
                audio: false,
                video: true
            }).then(stream => {

                // 将视频流写入到video标签中                
                localVideo.srcObject = stream;
                localStream = stream;

                stream.getTracks().forEach(track => {
                    pc1.addTrack(track, stream)
                })
                pc1.createOffer().then(sdp => {
                    pc1.setLocalDescription(sdp)
                    send({
                        type: 'offer',
                        data: sdp
                    })
                })
            })


        }

        function send(data) {
            data.toUserId = data.toUserId || to
            console.log({
                "name": "发送",
                data
            })
            socket.emit("exchange", JSON.stringify(data))
        }
    </script>
    <style>
        #video {
            background-color: black;
            height: 30vh;
        }
    </style>
</body>

</html>

WebRTC学习(六)端对端传输_51CTO博客_webrtc学习

webRTC结合webSocket实时通信 - 掘金

4、关于 coturn 信令服务的安装,参考以下文档:coturn安装以及报错"coturn/src/apps/relay/netengine.c:316:对'SSL_CTX_up_ref'未定义的引用"_coturn 启动报错_Mango酱的博客-CSDN博客

5、coturn 安装后的测试:Trickle ICE (webrtc.github.io)

6、代码中进行如下设置:

javascript 复制代码
  //该参数为了
        var iceServer = {
            // iceServers: [{
            //     urls: 'stun:stun.l.google.com:19302'
            // }]
            iceServers: [{
                    urls: 'stun:stun.services.mozilla.com'
                },
                {
                    urls: 'turn:ip:3478',
                    credential: '密码',
                    username: '账号'
                }
            ]
        };
相关推荐
itzilong15 小时前
webrtc支持h265
webrtc
内核工匠15 小时前
WebRTC音视频同步原理与实现详解(上)
音视频·webrtc
mo477616 小时前
webrtc音频模块介绍(二) ADM
音视频·webrtc
天涯学馆3 天前
WebRTC性能优化秘籍:打造高效稳定的音视频通信系统
前端·javascript·webrtc
EasyCVR7 天前
多品牌摄像机视频平台EasyCVR视频融合平台+应急布控球:打造城市安全监控新体系
大数据·网络·人工智能·音视频·webrtc
安步当歌10 天前
【WebRTC】视频发送链路中类的简单分析(上)
音视频·webrtc·视频编解码·video-codec
MetaverseMan11 天前
WebRTC 和 WebSocket
websocket·网络协议·webrtc
Mao.O11 天前
WebRTC实现双端音视频聊天(Vue3 + SpringBoot)
音视频·webrtc
红米饭配南瓜汤12 天前
WebRTC视频 03 - 视频采集类 VideoCaptureDS 上篇
音视频·webrtc·媒体
红米饭配南瓜汤13 天前
WebRTC视频 05 - 视频采集类 VideoCaptureDS 下篇
音视频·webrtc·媒体