webrtc分享屏幕或者canvas视频流,使用peerjs和peerjs-server设置

NO.1
服务端设置
安装peerjs-server
github地址:https://github.com/peers/peerjs-server
npm install peer -g

启动服务

peerjs --port 9000 --key peerjs --path /myapp

前端使用的配置为

<script>
    const peer = new Peer('someid', {
        host: 'localhost',
        path: '/myapp',
        port: 9000,
        key: `peerjs`,
    });
</script>
NO.2
前端页面
index.html为主页面,通过peerjs分享给副页面player.html
使用http-server启动本地服务
安装http-server
npm install http-server -g

启动

http-server

peerjs客户端库
github地址:https://github.com/peers/peerjs
文档地址:https://peerjs.com/
NO.3
index.html效果
使用localhost访问
本文地址:http://localhost:8081/
index.html代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>canvas webrtc</title>
    <script src="https://unpkg.com/[email protected]/dist/peerjs.min.js"></script>
    <style>
        .myCanvas {
            background-color: #fff;
        }
    </style>
</head>

<body>
    <div>canvas webrtc</div>
    <input id="aa" />
    <canvas id="myCanvas" class="myCanvas" width="200" height="100" style="border:1px solid #000000;"></canvas>
</body>
<script>
    // 模拟键盘点击事件
    function fireKeyEvent(el, evtType, keyCode) {
        var evtObj;
        if (document.createEvent) {
            if (window.KeyEvent) {//firefox 浏览器下模拟事件
                evtObj = document.createEvent('KeyEvents');
                evtObj.initKeyEvent(evtType, true, true, window, true, false, false, false, keyCode, 0);
            } else {//chrome 浏览器下模拟事件
                evtObj = document.createEvent('UIEvents');
                evtObj.initUIEvent(evtType, true, true, window, 1);

                delete evtObj.keyCode;
                if (typeof evtObj.keyCode === "undefined") {//为了模拟keycode
                    Object.defineProperty(evtObj, "keyCode", { value: keyCode });
                } else {
                    evtObj.key = String.fromCharCode(keyCode);
                }

                if (typeof evtObj.ctrlKey === 'undefined') {//为了模拟ctrl键
                    Object.defineProperty(evtObj, "ctrlKey", { value: true });
                } else {
                    evtObj.ctrlKey = true;
                }
            }
            el.dispatchEvent(evtObj);

        } else if (document.createEventObject) {//IE 浏览器下模拟事件
            evtObj = document.createEventObject();
            evtObj.keyCode = keyCode
            el.fireEvent('on' + evtType, evtObj);
        }
    }


    // 绘制边框
    function renderBorder(ctx) {
        // 首先获取画布的宽高
        const width = ctx.canvas.width;
        const height = ctx.canvas.height;
        ctx.beginPath(); // 开始一个新的路径
        ctx.moveTo(0, 0); // 将路径的起始点移动到左上角
        ctx.lineTo(width, 0); // 使用直线连接到右上角(并不绘制)
        ctx.lineTo(width, height); // ...右下角
        ctx.lineTo(0, height); // ...左下角
        ctx.closePath(); // 结束一个新的路径
        ctx.stroke(); // 绘制当前已知路径
        ctx.fillStyle = '#fff'
        ctx.fillRect(0, 0, width, height)
    }
    // 绘制一个球
    function renderBall(ctx, ball) {
        const x = ball.x + ball.radius; // 圆弧中心(圆心)的 x 轴坐标。
        const y = ball.y + ball.radius; // 圆弧中心(圆心)的 y 轴坐标。
        const radius = ball.radius; // 半径
        const startAngle = 0; // 圆弧的起始点
        const endAngle = 2 * Math.PI; // 圆弧的结束点
        ctx.beginPath(); // 开始一个新的路径
        ctx.arc(x, y, radius, startAngle, endAngle);
        ctx.closePath(); // 结束一个新的路径
        ctx.fillStyle = ball.color; // 颜色
        ctx.fill(); // 填充
    }
    // 更新ball
    function updateBall(ctx, ball) {
        const width = ctx.canvas.width;
        const height = ctx.canvas.height;
        ball.x += ball.vx;
        ball.y += ball.vy;
        ball.vy += ball.g;
        if (ball.y + ball.radius >= height) {
            ball.y = height - ball.radius;
            ball.vy = -ball.vy * 0.5;
        }
        if (ball.y + ball.radius <= 0) {
            ball.y = 0 - ball.radius;
            ball.vy = -ball.vy * 0.5;
        }
        if (ball.x + ball.radius >= width) {
            ball.x = width - ball.radius;
            ball.vx = -ball.vx * 0.5;
        }
        if (ball.x + ball.radius <= 0) {
            ball.x = 0 - ball.radius;
            ball.vx = -ball.vx * 0.5;
        }
        return ball;



    };

    window.onload = () => {
        // 监听当前页面键盘点击事件
        document.onkeydown = function (event) {
            var e = event || window.event
            console.log(`主页面键盘事件`, e.keyCode)
        };



        console.log(`设置canvas`)

        const canvas = document.getElementById('myCanvas')

        // 获取canvasDom
        // 手动设置宽高
        canvas.width = 800;
        canvas.height = 800;
        // 获取canvas上下文
        const ctx = canvas.getContext("2d");
        let ball = {
            x: 0, // 当前x轴坐标
            y: 0, // 当前y轴坐标
            radius: 10, // 半径
            g: 0.1, // 重力加速度
            vx: 8, // x轴移动速度
            vy: 4, // y轴移动速度
            color: "red", // 颜色
        };
        setInterval(() => {
            // 先将之前绘制的擦除
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

            // 绘制边框
            renderBorder(ctx);
            // 绘制球
            renderBall(ctx, ball);
            ball = updateBall(ctx, ball);
        }, 20);
    };

    // console.log(`连接peer`, Peer)






    // 需要建立webrtc服务
    // https://github.com/peers/peerjs-server

    // 浏览器使用
    // https://github.com/peers/peerjs


    var peer = null;
    var startId = null;
    var receiveId = null;
    var conn = null;
    let idStr = `123`

    peer = new Peer(idStr, {
        host: 'localhost',
        path: '/myapp',
        port:9000,
        key: `peerjs`,
    });


    peer.on('open', function (id) {
        startId = id;
        console.log('My peer ID is: ' + id);

    });

    peer.on('connection', function (conn) {
        console.log("被连接", conn);
        receiveId = conn.peer;
        conn.on('data', (data) => {
            console.log("收到消息", data, receiveId);
            if (data == `screen`) {
                // 获取当前屏幕视频流
                // navigator.mediaDevices.getDisplayMedia({ video: true })
                //     .then(stream => {
                //         // 成功回调的流,将它赋给video元素;
                //         console.log(`成功捕获屏幕数据`)
                //         conn6 = peer.connect(receiveId);
                //         conn6.on('open', () => {
                //             //直接发送消息
                //             console.log("发送消息 22")
                //             conn6.send("我来了 22");
                //             // videoElement.srcObject = stream;
                //             var call = peer.call(receiveId,
                //                 stream);
                //         });

                //     }, error => {
                //         console.log("Unable to acquire screen capture", error);
                //     });

                // 获取canvas画布的视频流
                var canvasElt = document.getElementById('myCanvas')
                var stream = canvasElt.captureStream();
                console.log(`成功捕获屏幕数据 canvas`)
                conn6 = peer.connect(receiveId);
                conn6.on('open', () => {
                    //直接发送消息
                    console.log("发送消息 22")
                    conn6.send("我来了 22");
                    stream.getTracks().forEach(track => {
                        console.log(`1111`)
                        // pc.addTrack(track, stream)
                        var call = peer.call(receiveId,
                            stream);
                    });

                });
            } else {
                console.log(`设置键盘事件`, data)
                fireKeyEvent(document.getElementById('aa'), 'keydown', data);
            }

        });

    });






</script>

</html>
NO.4
player.html效果
使用localhost访问
本文地址:http://localhost:8081/player.html?type=call&peerid=123
player.html代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>player</title>
    <script src="https://unpkg.com/[email protected]/dist/peerjs.min.js"></script>
    <style>
        .videoDom {
            width: 500px;
            height: 300px;
            border: 1px solid red;
        }
    </style>
</head>

<body>
    <div>player</div>
    <video id="videotest" class="videoDom" controls></video>
</body>
<script>
    // 获取参数
    function getQueryVariable(variable) {
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i = 0; i < vars.length; i++) {
            var pair = vars[i].split("=");
            if (pair[0] == variable) { return pair[1]; }
        }
        return (false);
    }
    // 类型(call为副屏玩家)
    let dataType = getQueryVariable(`type`)
    // 房间id(peerJs)
    let peerId = getQueryVariable(`peerid`)
    // 玩家角色(玩家一,玩家二,玩家三,玩家四)
    let playNum = getQueryVariable(`playNum`)
    if (dataType == `call`) {
        var peer = null;
        var startId = null;
        var receiveId = null;
        var conn = null;

        startId = peerId

        console.log(`type call`, peerId)
        peer = new Peer(``, {
            host: 'localhost',
            path: '/myapp',
            port: 9000,
            key: `peerjs`,
        });
        // console.log(peer)
        peer.on('open', function (id) {
            receiveId = id;
            console.log('My peer ID is: ' + id, startId);
            //这里先向start建立连接,让start知道这个端的id(reveivedId)
            conn = peer.connect(startId);
            conn.on('open', () => {
                //直接发送消息
                console.log("发送消息")
                conn.send("screen");
                document.onkeydown = function (event) {
                    var e = event || window.event
                    console.log(`e`, e.keyCode, e)
                    conn.send(e.keyCode);
                };
            });
        });
        peer.on('connection', function (conn) {
            console.log("被连接3333");
            conn.on('data', (data) => {
                console.log("收到消息222", data);
            });
            peer.on('call', call => {
                console.log(' peer call');
                call.answer();
                call.on('stream', stream => {
                    // somthing to do
                    let videoDom = document.getElementById("videotest")
                    console.log(`接收到视频流`, videoDom, stream)
                    videoDom.srcObject = stream
                });
            });
        });




    } 
</script>

</html>
NO.5
Tips
本文主页面peerid强制设置为123,副页面通过url获取对应的peerid连接
  1. webrtc通信,可以使用localhost或者https域名,建议不要使用ip访问
  2. 本文演示效果为主页面canvas或者chrome tab页面视频流分享给副页面(具体内容见代码实现)
  3. 使用webrtc协议(如本文的peerjs库),可以实现如nes,街机游戏在线联机对战(已实现,本文不放置代码)
END.