知道吗?网页程序也可以跨程序(窗口)录屏

yunke 提交于 周三, 11/02/2022 - 14:09

在网页里面运行的JS程序,也是可以做到跨程序录屏的,比如录制QQ聊天界面、PPT展示等等,作为资深开发者,你可能会立即思考JS能力和权限的问题,它怎么可能逃出浏览器,访问到其他程序了呢?这仰仗WebRTC技术,在用户授权下可安全使用。

演示示例如下:
屏幕录屏:
加载的JS文件如下:


const videoElem = document.getElementById("video");
const logElem = document.getElementById("log");
const startElem = document.getElementById("start");
const stopElem = document.getElementById("stop");

// Options for getDisplayMedia()

var displayMediaOptions = {
    video: {
        cursor: "always"
    },
    audio: false
};

// Set event listeners for the start and stop buttons
startElem.addEventListener("click", function (evt) {
    startCapture();
}, false);

stopElem.addEventListener("click", function (evt) {
    stopCapture();
}, false);

console.log = msg => logElem.innerHTML += `${msg}<br>`;
console.error = msg => logElem.innerHTML += `<span class="error">${msg}</span><br>`;
console.warn = msg => logElem.innerHTML += `<span class="warn">${msg}<span><br>`;
console.info = msg => logElem.innerHTML += `<span class="info">${msg}</span><br>`;

async function startCapture() {
    logElem.innerHTML = "";

    try {
        videoElem.srcObject = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
        dumpOptionsInfo();
    } catch (err) {
        console.error("Error: " + err);
    }
}

function stopCapture(evt) {
    let tracks = videoElem.srcObject.getTracks();

    tracks.forEach(track => track.stop());
    videoElem.srcObject = null;
}

function dumpOptionsInfo() {
    const videoTrack = videoElem.srcObject.getVideoTracks()[0];

    console.info("Track settings:");
    console.info(JSON.stringify(videoTrack.getSettings(), null, 2));
    console.info("Track constraints:");
    console.info(JSON.stringify(videoTrack.getConstraints(), null, 2));
}

HTML内容如下:

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <style>
        body {
            padding: 0;
            margin: 0;
        }
        svg:not(:root) {
            display: block;
        }
        .playable-code {
            background-color: #f4f7f8;
            border: none;
            border-left: 6px solid #558abb;
            border-width: medium medium medium 6px;
            color: #4d4e53;
            height: 100px;
            width: 90%;
            padding: 10px 10px 0;
        }
        .playable-canvas {
            border: 1px solid #4d4e53;
            border-radius: 2px;
        }
        .playable-buttons {
            text-align: right;
            width: 90%;
            padding: 5px 10px 5px 26px;
        }
    </style>
    <style>
        #video {
            border: 1px solid #999;
            width: 98%;
            max-width: 860px;
        }
        .error {
            color: red;
        }
        .warn {
            color: orange;
        }
        .info {
            color: darkgreen;
        }
    </style>
    <title>使用屏幕捕获 API - simple_screen_capture - code sample</title>
  </head>
  <body>
    <button id="start">Start Capture</button>&nbsp;<button id="stop">Stop Capture</button>
    <video id="video" autoplay="autoplay"></video>
    <br>
    <strong>Log:</strong>
    <br>
    <pre id="log"></pre>
    <script async src="capture.js"></script>
  </body>
</html>

访问以上网页试一试

文件录制:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>MediaCapture and Streams API</title>
    <meta name="viewport" content="width=device-width">
</head>
<body>
<header>
    <h1>媒体捕获、媒体记录器和流API</h1>
</header>
<main>

    <p><button id="btnStart">开始录制</button><br/>
        <button id="btnStop">停止录制</button></p>

    <video controls></video>

    <video id="vid2" controls></video>

</main>
<script>

    let constraintObj = {
        audio: false,
        video: {
            facingMode: "user",
            width: { min: 640, ideal: 1280, max: 1920 },
            height: { min: 480, ideal: 720, max: 1080 }
        }
    };

    //getUserMedia旧浏览器兼容
    if (navigator.mediaDevices === undefined) {
        navigator.mediaDevices = {};
        navigator.mediaDevices.getUserMedia = function(constraintObj) {
            let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
            if (!getUserMedia) {
                return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
            }
            return new Promise(function(resolve, reject) {
                getUserMedia.call(navigator, constraintObj, resolve, reject);
            });
        }
    }else{
        navigator.mediaDevices.enumerateDevices()
            .then(devices => {
                devices.forEach(device=>{
                    console.log(device.kind.toUpperCase(), device.label);
                })
            })
            .catch(err=>{
                console.log(err.name, err.message);
            })
    }

    navigator.mediaDevices.getUserMedia(constraintObj)
        .then(function(mediaStreamObj) {
            let video = document.querySelector('video');
            if ("srcObject" in video) {
                video.srcObject = mediaStreamObj;
            } else {
                video.src = window.URL.createObjectURL(mediaStreamObj);
            }

            video.onloadedmetadata = function(ev) {
                video.play();
            };

            let start = document.getElementById('btnStart');
            let stop = document.getElementById('btnStop');
            let vidSave = document.getElementById('vid2');
            let mediaRecorder = new MediaRecorder(mediaStreamObj);
            let chunks = [];

            start.addEventListener('click', (ev)=>{
                mediaRecorder.start();
                console.log(mediaRecorder.state);
            })
            stop.addEventListener('click', (ev)=>{
                mediaRecorder.stop();
                console.log(mediaRecorder.state);
            });
            mediaRecorder.ondataavailable = function(ev) {
                chunks.push(ev.data);
            }
            mediaRecorder.onstop = (ev)=>{
                let blob = new Blob(chunks, { 'type' : 'video/mp4;' });
                chunks = [];
                let videoURL = window.URL.createObjectURL(blob);
                vidSave.src = videoURL;
            }
        })
        .catch(function(err) {
            console.log(err.name, err.message);
        });
</script>
</body>
</html>

该技术起源于谷歌,被用于基于网页的视频会议系统

添加新评论

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。
请输入以上问题的答案,换一个问题请刷新,不区分大小写