Chrome录制电脑视频,保存

@Ta 2021-04-16发布,2021-04-16修改 6894点击
chrome有API接口,可以获取摄像头和麦克风。理论上可以做 语音/视频聊天/ 浏览器直播主播端。
因为网上录制视频的软件都要钱,而且 怕有毒,所以用chrome撸了一个。浏览器打开,录制完毕直接下载导出


 

视频链接




<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>本地屏幕录制</title>
    <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css">

    <!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
    <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>

    <!-- bootstrap.bundle.min.js 用于弹窗、提示、下拉菜单,包含了 popper.min.js -->
    <script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script>

    <!-- 最新的 Bootstrap4 核心 JavaScript 文件 -->
    <script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body style="height: 100%">



    <div style="display: flex;justify-content: center;align-items: center;margin-top: 10vh">
        <div style="text-align: center;background-color: #9a9a9a;width: 100%;display: block">
            <video  id="livePlayer"   style="max-width: 800px;width: 100%;min-height: 50%" ></video>
            <audio  id="liveAudio"></audio>

            <div>
                <video id="recordPlayback"
                        style="max-width: 800px;width: 100%;min-height: 50%"
                        controls playsinline autoplay hidden >
                </video>
            </div>

        </div>


    </div>

    <div style="text-align: center">
        <div style="display: block;">
            <div id="recordControls">
                <p>
                    <input name="closePlayerAudio" value="1" checked type="radio"/>回声消除

                    <input name="closePlayerAudio" value="0"  type="radio"/>播放回声

                    <label for="frameRate">帧数FPS</label>
                    <input id="frameRate" value="100" type="text" />
                </p>
                <span id="recordStatus"></span>
                <button class="recordBtn btn btn-primary" id="recordStart">开始</button>
                <button class="recordBtn btn btn-warning" id="recordPause" disabled>暂停</button>
                <button class="recordBtn  btn btn-danger" id="recordStop" disabled>结束</button>
                <button class="recordBtn btn  btn-info" id="recordPlay" disabled>播放</button>
                <button class="recordBtn btn  btn-success" id="recordSave" disabled>下载</button>
            </div>
        </div>
    </div>


</body>
<script >




    let recorder;
    let recordingData = [];
    let recorderStream;

    /**
     * 混合音频
     * */
    function mixer(stream1, stream2) {
        const ctx = new AudioContext();
        const dest = ctx.createMediaStreamDestination();

        if(stream1.getAudioTracks().length > 0)
            ctx.createMediaStreamSource(stream1).connect(dest);

        if(stream2.getAudioTracks().length > 0)
            ctx.createMediaStreamSource(stream2).connect(dest);

        let tracks = dest.stream.getTracks();
        tracks = tracks.concat(stream1.getVideoTracks()).concat(stream2.getVideoTracks());

        return new MediaStream(tracks)

    }

    /**
     * 根据URL和时间戳中的Jitsi房间名称返回文件名
     *
     */
    function getFilename(){
        const now = new Date();
        const timestamp = now.toISOString();
        const room = new RegExp(/(^.+)\s\|/).exec(document.title);
        if(room && room[1]!=="")
            return `${room[1]}_${timestamp}`;
        else
            return `recording_${timestamp}`;
    }

    /**
     * 检查浏览器支持的编码
     *
     */
    function checkCodecsSupported(){
        let options = {mimeType: 'video/webm;codecs=vp9,opus'};
        if (!MediaRecorder.isTypeSupported(options.mimeType)) {
            console.error(`${options.mimeType} is not supported`);
            options = {mimeType: 'video/webm;codecs=vp8,opus'};
            if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                console.error(`${options.mimeType} is not supported`);
                options = {mimeType: 'video/webm'};
                if (!MediaRecorder.isTypeSupported(options.mimeType)) {
                    console.error(`${options.mimeType} is not supported`);
                    options = {mimeType: ''};
                }
            }
        }
        return options;
    }

    /**
     * 开始录制
     * */
    const start = document.getElementById('recordStart');
    const frameRate = document.getElementById('frameRate');

    start.addEventListener('click', async ()=> {

        const options = checkCodecsSupported();

        let gumStream, gdmStream;
        recordingData = [];

        try {
            gumStream = await navigator.mediaDevices.getUserMedia({video: false, audio: {
                    echoCancellation: {exact: true}
                }});
            gdmStream = await navigator.mediaDevices.getDisplayMedia({video: {width: 1280, height: 720,displaySurface: "browser",frameRate: frameRate.value > 0 ? frameRate.value : 15}, audio: {
                    echoCancellation: true
                }})


        } catch (e) {
            console.error("capture failure", e);
            return
        }

        recorderStream = gumStream ? mixer(gumStream, gdmStream) : gdmStream;


        var liveVideo=document.getElementById("livePlayer");
        liveVideo.srcObject = gdmStream;
        liveVideo.play();


        let radio=document.getElementsByName("closePlayerAudio");

        radio.forEach(v=>{
            if(v.checked==true){
                if(v.value==0){
                    var liveAudio=document.getElementById("liveAudio");
                    liveAudio.srcObject = recorderStream;
                    liveAudio.play();
                }
            }
        })




        recorder = new MediaRecorder(recorderStream, options);

        recorder.ondataavailable = e => {
            if (e.data && e.data.size > 0) {
                recordingData.push(e.data);
            }
        };

        recorder.onStop = () => {
            recorderStream.getTracks().forEach(track => track.stop());
            gumStream.getTracks().forEach(track => track.stop());
            gdmStream.getTracks().forEach(track => track.stop());

        };

        recorderStream.addEventListener('inactive', () => {
            console.log('Capture stream inactive');
            stopCapture();
        });

        recorder.start();
        console.log("started recording");
        start.innerText = "录屏中..";

        start.disabled = true;
        pause.disabled = false;
        stop.disabled = false;
        play.disabled = true;
        save.disabled = true;
    });


    /**
     * 停止录制
     */
    const stop = document.getElementById('recordStop');
    function stopCapture(){
        console.log("Stopping recording");
        recorder.stop();

        start.disabled = false;
        pause.disabled = true;
        stop.disabled = true;
        play.disabled = false;
        save.disabled = false;

        start.innerText = "开始";
        pause.innerText = "暂停";

    }
    stop.addEventListener('click', stopCapture);

    /**
     * 暂停录制
     */
    const pause = document.getElementById('recordPause');
    pause.addEventListener('click', ()=>{
        if(recorder.state ==='paused'){
            recorder.resume();
            pause.innerText = "暂停"
        }
        else if (recorder.state === 'recording'){
            recorder.pause();
            pause.innerText = "继续"

        }
        else
            console.error(`recorder in unhandled state: ${recorder.state}`);

        console.log(`recorder ${recorder.state === 'paused' ? "paused" : "recording"}`);

    });

    /**
     *
     *  播放录制
     */
    let isPlaying = false;
    const play = document.getElementById('recordPlay');
    play.addEventListener('click', ()=>{
        var p=document.getElementById("livePlayer");
        playback.hidden = !playback.hidden;
        p.hidden=!playback.hidden;
        if (!isPlaying && !playback.hidden){
            playback.src = window.URL.createObjectURL(new Blob(recordingData, {type: 'video/webm'}));
            playback.play();
            play.innerText = "停止播放";
        }
        else{
            play.innerText = "播放录制";
        }

    });

    const playback = document.getElementById('recordPlayback');
    // 添加处理事件
    playback.addEventListener('play', ()=>{isPlaying = true});
    playback.addEventListener('pause', ()=>{isPlaying = false});
    playback.addEventListener('playing', ()=>{isPlaying = true});
    playback.addEventListener('ended', ()=>{isPlaying = false});

    /**
     * 保存录制
     * */
    const save = document.getElementById('recordSave');
    save.addEventListener('click', () => {
        const blob = new Blob(recordingData, {type: 'video/webm'});
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = url;
        a.download = `${getFilename()}.webm`;
        document.body.appendChild(a);
        a.click();
        setTimeout(() => {
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
            console.log(`${a.download} save option shown`);
        }, 100);
    });

</script>
</html>



(客户端)
回复列表(12|显示机器人聊天)
  • @Ta / 2021-04-16 / /
    image.png


    image.png
  • @Ta / 2021-04-17 / /

    哎呀真大神呀。好奇能不能录浏览器以外的?还是只能录浏览器操作
    小米8(白)

  • @Ta / 2021-04-17 / /

    厉害
    小米MIX2s(白)

  • @Ta / 2021-04-17 / /

    obs studio

    ============

    楼主真是神豪,,用mac
    我们有着共同的敌人——天命;
    你却认命了,还对我说天命不可违‮

  • @Ta / 2021-04-17 / /

    https://www.jianguoyun.com/p/Dfttx3sQv7TOCBjZ3O0D
    我们有着共同的敌人——天命;
    你却认命了,还对我说天命不可违‮

  • @Ta / 2021-04-17 / /

    @胡椒舰长,大部分主播和UP主都使用OBS Studio进行录制和推流,也推荐你使用。

  • @Ta / 2021-04-17 / /

    只要看一个主播的直播看得久,总有机会看到他调OBS Studio窗口。PDD、装机猿等知名主播都在直播期间调整过他们的OBS Studio窗口,足见该软件的市场占有率多高。

    不过不用担心该软件处于市场支配地位有什么不好之处,因为它和Linux内核一样是以GPL协议开源的自由软件,所有人都可以自由获取软件的安装包和源代码并用于任何用途。

    比如,B站的官方直播软件“直播姬”就是OBS改的,而且他们也按GPL的要求提供了修改后的源代码。

  • @Ta / 2021-04-17 / /

    OBS不仅可以直播推流,也支持视频录制。无论是摄像头、采集卡还是屏幕录制,OBS都能给出专业的解决方案,就算三者同时进行,同时再推流,OBS也能出色完成。

    此外,OBS还支持GPU加速视频编码,无论是A/N/I哪家的显卡/集显都能兼容。显卡的视频编码加速通常使用专用电路实现(和3D不是同一个处理器),所以启用后相当于OBS视频编码既不占用CPU也不占用显卡3D图形性能,在录制游戏时对性能的影响就可以降到最小。对于CPU性能一般又不想搭建双机采集环境的主播来说该功能相当重要。如果直接使用CPU编码视频,可能他们根本没办法流畅播出游戏,因为视频编码会占用大部分CPU,导致游戏卡顿。

  • @Ta / 2021-04-17 / /

    而且最好的一点是,OBS支持Windows、Linux、macOS。所以无论你使用什么电脑、什么操作系统,你总有机会用上它。

    免费的,最不贵。大主播都在用。

  • @Ta / 2021-04-17 / /
    @艾木友尔尔巴,这个本来就是可以录制浏览器以外的屏幕的,还可以做到录制指定软件,其他软件图层覆盖的时候也不会把其他软件内容录制进去

    @老虎会游泳,了解,我一直用的付费版,花了不少学费
  • @Ta / 2021-04-17 / /

    牛逼
    小米8高配版(五颜六色)

  • @Ta / 2021-04-18 / /

    @胡椒舰长,你用过什么付费版

添加新回复
回复需要登录