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>
posted @ 2024-03-08 14:42  vx_guanchaoguo0  阅读(8)  评论(0编辑  收藏  举报