首页 > 其他分享 >WebRTC 同一个浏览器同一台 无需信令服务 实现视频通话

WebRTC 同一个浏览器同一台 无需信令服务 实现视频通话

时间:2024-03-08 14:58:06浏览次数:20  
标签:const log offer window peer 浏览器 message WebRTC 信令

这个仅仅是原理 告你信令服务的作用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>p2p webrtc</title>
    <style>
        .container {
            width: 250px;
            margin: 100px auto;
            padding: 10px 30px;
            border-radius: 4px;
            border: 1px solid #ebeef5;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
            color: #303133;
        }
    </style>
</head>
<body>
<div class="container">
    <p>流程:</p>
    <ul>
        <li>打开<a href="/answer" target="_blank">接收方页面</a>;</li>
        <li>打开<a href="/offer" target="_blank">发起方页面</a>;</li>
        <li>确认双方都已建立连接;</li>
        <li>发起方点击 start 按钮。</li>
    </ul>
</div>
</body>
<script>
    window.localStorage.clear();
</script>
</html>

发起端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <style>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }

        .container {
            width: 100%;
            display: flex;
            display: -webkit-flex;
            justify-content: space-around;
            padding-top: 20px;
        }

        .video-box {
            position: relative;
            width: 800px;
            height: 400px;
        }

        #remote-video {
            width: 100%;
            height: 100%;
            display: block;
            object-fit: cover;
            border: 1px solid #eee;
            background-color: #F2F6FC;
        }

        #local-video {
            position: absolute;
            right: 0;
            bottom: 0;
            width: 240px;
            height: 120px;
            object-fit: cover;
            border: 1px solid #eee;
            background-color: #EBEEF5;
        }

        .start-button {
            position: absolute;
            left: 50%;
            top: 50%;
            width: 100px;
            display: none;
            line-height: 40px;
            outline: none;
            color: #fff;
            background-color: #409eff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transform: translate(-50%, -50%);
        }

        .logger {
            width: 40%;
            padding: 14px;
            line-height: 1.5;
            color: #4fbf40;
            border-radius: 6px;
            background-color: #272727;
        }

        .logger .error {
            color: #DD4A68;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="video-box">
        <video id="remote-video"></video>
        <video id="local-video" muted></video>
        <button class="start-button" onclick="startLive()">start</button>
    </div>
    <div class="logger"></div>
</div>
<script>
    const message = {
        el: document.querySelector('.logger'),
        log(msg) {
            this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
        },
        error(msg) {
            this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
        }
    };

    const localVideo = document.querySelector('#local-video');
    const remoteVideo = document.querySelector('#remote-video');
    const button = document.querySelector('.start-button');

    localVideo.onloadeddata = () => {
        message.log('播放本地视频');
        localVideo.play();
    }
    remoteVideo.onloadeddata = () => {
        message.log('播放对方视频');
        remoteVideo.play();
    }

    document.title = '发起方';

    message.log('信令通道(localStorage)创建中......');
    message.log('信令通道创建成功!');
    button.style.display = 'block';

    window.addEventListener('storage', e => {
        if (e.key === 'answer') {
            const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
            if (type === 'answer') {
                peer.setRemoteDescription(new RTCSessionDescription({type, sdp}));
                message.log('offer : \ntype answer  setRemoteDescription ');
            }
        }
        if (e.key === 'answer_ice') {
            const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
            if (type === 'answer_ice') {
                peer.addIceCandidate(iceCandidate);
                message.log('offer : \ntype answer_ice  addIceCandidate ');
            }
        }
    })


    const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    !PeerConnection && message.error('浏览器不支持WebRTC!');
    const peer = new PeerConnection();

    peer.ontrack = e => {
        if (e && e.streams) {
            message.log('收到对方音频/视频流数据...');
            remoteVideo.srcObject = e.streams[0];
        }
    };

    peer.onicecandidate = e => {
        if (e.candidate) {
            message.log('搜集并发送候选人');
            window.localStorage.setItem('offer_ice', JSON.stringify({
                type: `offer_ice`,
                iceCandidate: e.candidate
            }));
        } else {
            message.log('候选人收集完成!');
        }
    };

    async function startLive() {
        let stream;
        try {
            message.log('尝试调取本地摄像头/麦克风');
            stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
            message.log('摄像头/麦克风获取成功!');
            localVideo.srcObject = stream;
        } catch {
            message.error('摄像头/麦克风获取失败!');
            return;
        }

        message.log(`------ WebRTC发起方流程开始 ------`);
        message.log('将媒体轨道添加到轨道集');
        stream.getTracks().forEach(track => {
            peer.addTrack(track, stream);
        });


        window.localStorage.setItem('offer', '')
        window.localStorage.setItem('offer_ice', '');
        message.log('创建本地SDP');
        const offer = await peer.createOffer();
        await peer.setLocalDescription(offer);

        message.log(`传输发起方本地SDP`, JSON.stringify(offer));

        window.localStorage.setItem('offer', JSON.stringify(offer));

    }
</script>
</body>
</html>

接受端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title></title>
    <style>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }

        .container {
            width: 100%;
            display: flex;
            display: -webkit-flex;
            justify-content: space-around;
            padding-top: 20px;
        }

        .video-box {
            position: relative;
            width: 800px;
            height: 400px;
        }

        #remote-video {
            width: 100%;
            height: 100%;
            display: block;
            object-fit: cover;
            border: 1px solid #eee;
            background-color: #F2F6FC;
        }

        #local-video {
            position: absolute;
            right: 0;
            bottom: 0;
            width: 240px;
            height: 120px;
            object-fit: cover;
            border: 1px solid #eee;
            background-color: #EBEEF5;
        }

        .start-button {
            position: absolute;
            left: 50%;
            top: 50%;
            width: 100px;
            display: none;
            line-height: 40px;
            outline: none;
            color: #fff;
            background-color: #409eff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transform: translate(-50%, -50%);
        }

        .logger {
            width: 40%;
            padding: 14px;
            line-height: 1.5;
            color: #4fbf40;
            border-radius: 6px;
            background-color: #272727;
        }

        .logger .error {
            color: #DD4A68;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="video-box">
        <video id="remote-video"></video>
        <video id="local-video" muted></video>
    </div>
    <div class="logger"></div>
</div>
<script>
    const message = {
        el: document.querySelector('.logger'),
        log(msg) {
            this.el.innerHTML += `<span>${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
        },
        error(msg) {
            this.el.innerHTML += `<span class="error">${new Date().toLocaleTimeString()}:${msg}</span><br/>`;
        }
    };
    const localVideo = document.querySelector('#local-video');
    const remoteVideo = document.querySelector('#remote-video');


    localVideo.onloadeddata = () => {
        message.log('播放本地视频');
        localVideo.play();
    }
    remoteVideo.onloadeddata = () => {
        message.log('播放对方视频');
        remoteVideo.play();
    }

    document.title = '接收方';

    message.log('信令通道(localStorage)创建中......');
    message.log('信令通道创建成功!');

    window.addEventListener('storage', e => {
        if (e.key === 'offer') {
            const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
            if (type === 'offer') {
                startLive(new RTCSessionDescription({type, sdp}));
                message.log('answer : \ntype   offer startLive  ');
            }
        }
        if (e.key === 'offer_ice') {
            const {type, sdp, iceCandidate} = JSON.parse(e.newValue)
            if (type === 'offer_ice') {
                peer.addIceCandidate(iceCandidate);
                message.log('answer  : \ntype offer_ice  addIceCandidate ');
            }
        }
    })


    const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
    !PeerConnection && message.error('浏览器不支持WebRTC!');
    const peer = new PeerConnection();

    peer.ontrack = e => {
        if (e && e.streams) {
            message.log('收到对方音频/视频流数据...');
            remoteVideo.srcObject = e.streams[0];
        }
    };

    peer.onicecandidate = e => {
        if (e.candidate) {
            message.log('搜集并发送候选人');
            window.localStorage.setItem('answer_ice', JSON.stringify({
                type: `answer_ice`,
                iceCandidate: e.candidate
            }));
        } else {
            message.log('候选人收集完成!');
        }
    };

    async function startLive(offerSdp) {
        let stream;
        try {
            message.log('尝试调取本地摄像头/麦克风');
            stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
            message.log('摄像头/麦克风获取成功!');
            localVideo.srcObject = stream;
        } catch {
            message.error('摄像头/麦克风获取失败!');
            return;
        }

        message.log(`------ WebRTC接收方流程开始 ------`);
        message.log('将媒体轨道添加到轨道集');
        stream.getTracks().forEach(track => {
            peer.addTrack(track, stream);
        });


        message.log('接收到发送方SDP');
        window.localStorage.setItem('answer', '');
        window.localStorage.setItem('answer_ice', '');
        await peer.setRemoteDescription(offerSdp);

        message.log('创建接收方(应答)SDP');
        const answer = await peer.createAnswer();
        message.log(`传输接收方(应答)SDP`);
        window.localStorage.setItem('answer', JSON.stringify(answer));
        await peer.setLocalDescription(answer);

    }
</script>
</body>
</html>

标签:const,log,offer,window,peer,浏览器,message,WebRTC,信令
From: https://www.cnblogs.com/guanchaoguo/p/18060932

相关文章

  • node和浏览器发送请求的方式
    浏览器请求接口运行在客户端的浏览器环境中,而Node请求接口运行在服务器端的Node.js环境中。浏览器提供了特定的API(如FetchAPI或XMLHttpRequest)来发送HTTP请求fetch:FetchAPI是一种现代、基于Promise的JavaScriptAPI,用于在浏览器环境中执行网络请求fetch(url,{m......
  • 浏览器和服务器之前的加密解密过程
    当服务器通过HTTPS发送加密数据时,浏览器会进行以下步骤来解密这些数据:建立HTTPS连接:用户在浏览器中输入HTTPS网址,浏览器会尝试与服务器建立连接。这个连接是通过HTTPS协议进行的,它使用SSL/TLS加密技术来确保数据的安全性。交换密钥和证书:在建立连接的过程中,服务器会向浏......
  • WEBRTC 局域网 自己搭建信令服务 实现视频通讯
    信令服务constapp=require('express')();constwsInstance=require('express-ws')(app);app.ws('/',ws=>{ ws.on('message',data=>{ wsInstance.getWss().clients.forEach(server=>{ if(server!==ws)......
  • WebRTC 通讯隧道和信令服务实现服务视频通话
    安装NAT穿透服务器(ICEServer)brewinstallcoturn//添加用户turnadmin-a-uadmin-rrealm-padmin//测试服务turnutils_peer-p34800turnutils_uclient-v-e192.168.1.112-r34800-uadmin-wadmin-p3478192.168.1.112安装信令服务器gitclonehttps......
  • 如何使文件直接在浏览器中预览或下载
     如何使文件直接在浏览器中预览,而不是下载?首先请正确配置Content-Type头部,并且文件的Content-Disposition头部的参数值设置为inline(表示浏览器应该尝试打开内容),当浏览器支持打开当前文件的格式时,浏览器会直接打开该文件,而不是直接下载。如何使文件直接在浏览器中下载,......
  • windows编译ZLMediaKit流媒体服务webrtc
    环境说明ZLMediaKit编译需要的软件visualstudio2022cmake3.29.0-rc2OpenSSL1.1.1w(不想踩坑的话安装这个版本)libsrtp2.6.0ZLMediaKit编译后运行需要libsrtp编译后且配置环境变量ZLMediaKit编译后文件visualstudiocmakevisualstuid......
  • 网页浏览器Chrome开发者调试工具-Application(应用程序)
    前言全局说明网页浏览器Chrome开发者调试工具-Application(应用程序)一、网页浏览器Chrome开发者调试工具-Application(应用程序)Application:应用面板,用于记录网站加载的所有资源信息,如存储、缓存、字体、图片等,同时也可以对一些资源进行修改和删除。二、关闭标签页......
  • 网页浏览器Chrome开发者调试工具-Network(网络)
    前言全局说明网页浏览器Chrome开发者调试工具-Network(网络)一、网页浏览器Chrome开发者调试工具-Network(网络)网络标签页是对网页请求过程的监视,这里可以看到网页链接发送了什么请求,接收到了什么内容。都可以直观的看到二、关闭标签页在标签页上右键,可以选择移除或移......
  • 网页浏览器Chrome开发者调试工具-Performance(性能)
    前言全局说明网页浏览器Chrome开发者调试工具-Performance(性能)一、网页浏览器Chrome开发者调试工具-Performance(性能)用于记录和分析页面在运行时的所有活动,比如CPU占用情况、呈现页面的性能分析二、关闭标签页在标签页上右键,可以选择移除或移动到快速视图栏免责......
  • 网页浏览器Chrome开发者调试工具-Memory(内存)
    前言全局说明网页浏览器Chrome开发者调试工具-Memory(内存)一、网页浏览器Chrome开发者调试工具-Memory(内存)内存面板,用于记录和分析页面占用内存的情况,如查看内存占用变化,查看JavaScript对象和HTML节点的内存分配。二、关闭标签页在标签页上右键,可以选择移除或移动......