首页 > 其他分享 >前端录屏并保存视频到本地

前端录屏并保存视频到本地

时间:2024-04-12 23:15:34浏览次数:20  
标签:视频 const 前端 录屏 video hidden true recorder

API、第三方库使用 getDisplayMedia + MediaRecorder + @ozean/set-webm-duration

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>前端录屏并保存视频到本地</title>
    <style>
      canvas,
      video {
        width: 640px;
        /* height: 300px; */
        aspect-ratio: 16 / 9;
        border: 1px solid black;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <h3>前端录屏并保存视频到本地</h3>
    <button type="button" id="share-screen">开启屏幕共享</button>
    <button type="button" hidden id="close-screen">关闭屏幕共享</button>
    <button type="button" hidden id="record-start">录屏</button>
    <button type="button" hidden id="record-pause">暂停录屏</button>
    <button type="button" hidden id="record-resume">继续录屏</button>
    <button type="button" hidden id="record-stop">停止录屏</button>
    <button type="button" hidden id="record-download">下载录屏</button>
    <hr />

    <script type="module">
      // 下载依赖 npm i @ozean/set-webm-duration
      import { setWebmDuration } from '../node_modules/@ozean/set-webm-duration/dist/set-webm-duration.esm.js';

      const shareScreen = document.getElementById('share-screen');
      const closeScreen = document.getElementById('close-screen');
      const recordStart = document.getElementById('record-start');
      const recordPause = document.getElementById('record-pause');
      const recordResume = document.getElementById('record-resume');
      const recordStop = document.getElementById('record-stop');
      const recordDownload = document.getElementById('record-download');

      const constraints = { video: true, audio: true };
      /** @type {MediaRecorder} */
      let recorder = null,
        videoDuration = 0;
      const chunks = [];

      shareScreen.addEventListener('click', shareScreenHandler);
      closeScreen.addEventListener('click', closeScreenHandler);

      recordStart.addEventListener('click', recordStartHandler);
      recordStop.addEventListener('click', recordStopHandler);
      recordPause.addEventListener('click', recordPauseHandler);
      recordResume.addEventListener('click', recordResumeHandler);

      recordDownload.addEventListener('click', downloadVideo);

      // 开启录屏
      function recordStartHandler() {
        if (!recorder) throw new Error('未启动录屏');

        recorder.start();
        recordStart.setAttribute('hidden', true);
        recordStop.removeAttribute('hidden');
        recordPause.removeAttribute('hidden');
        console.log('开始录屏');
      }
      // 停止录屏
      function recordStopHandler() {
        if (!recorder) throw new Error('未启动录屏');

        recorder.stop();
        recordStop.setAttribute('hidden', true);
        recordPause.setAttribute('hidden', true);
        recordResume.setAttribute('hidden', true);
        closeScreen.setAttribute('hidden', true);
        recordDownload.removeAttribute('hidden');
      }
      // 暂停录屏
      function recordPauseHandler() {
        if (!recorder) throw new Error('未启动录屏');

        recorder.pause();
        recordPause.setAttribute('hidden', true);
        recordResume.removeAttribute('hidden');
      }
      // 继续录屏
      function recordResumeHandler() {
        if (!recorder) throw new Error('未启动录屏');

        recorder.resume();
        recordPause.removeAttribute('hidden');
        recordResume.setAttribute('hidden', true);
      }

      // 关闭屏幕共享
      let closed = false;
      function closeScreenHandler() {
        if (!recorder) throw new Error('屏幕共享未开启');

        closed = true;

        // 共享屏幕,未开启录屏
        if (recorder.state === 'inactive') {
          recordStart.setAttribute('hidden', true);
          _close();
          return;
        }

        const result = confirm('关闭屏幕共享后,录制的内容有可能丢失,确定要关闭屏幕共享吗?');
        if (result) {
          recordStop.setAttribute('hidden', true);
          recordPause.setAttribute('hidden', true);
          recordResume.setAttribute('hidden', true);
          recordDownload.setAttribute('hidden', true);
          _close();
        }
      }

      function _close() {
        closeScreen.setAttribute('hidden', true);
        removeVideo();
        recorder.stream.getTracks().forEach((track) => track.stop());
        recorder = null;
      }

      function removeVideo() {
        [...document.querySelectorAll('video')].forEach((dom) => dom.remove());
      }

      // 共享屏幕
      async function shareScreenHandler() {
        try {
          removeVideo();
          closed = false;

          let stream = await navigator.mediaDevices.getDisplayMedia(constraints);

          let video = document.createElement('video');
          video.playsInline = true;
          video.autoplay = true;
          video.muted = true;
          video.id = 'share-video';
          document.body.appendChild(video);

          // 显示共享屏幕内容
          video.srcObject = stream;

          closeScreen.removeAttribute('hidden');
          recordStart.removeAttribute('hidden');
          recordDownload.setAttribute('hidden', true);

          recorder = new MediaRecorder(stream, {
            mimeType: 'video/webm',
          });

          recorder.addEventListener('dataavailable', (event) => {
            chunks.push(event.data);
          });
          recorder.addEventListener('stop', () => {
            if (closed) return;

            const blob = new Blob(chunks, { type: 'video/webm' });
            const url = URL.createObjectURL(blob);
            console.log('录制结束');

            const video2 = document.createElement('video');
            video2.width = parseInt(getComputedStyle(video).width);
            video2.height = parseInt(getComputedStyle(video).height);
            video.playsInline = true;
            video2.muted = true;
            video2.id = 'record-video';
            video2.loop = true;

            video2.addEventListener('durationchange', setVideoDuration.bind(video2));

            video2.src = url;
            video2.currentTime = 24 * 60 * 60;
            // video2.play();

            // 录制结束,关闭录屏、track
            video.srcObject = null;
            video.style.display = 'none';
            stream.getTracks().forEach((track) => track.stop());
            video = stream = recorder = null;

            document.body.appendChild(video2);
            // 绘制录屏内容 (到 canvas)
            // drawVideo(video2);
          });

          // drawVideo(video);
          // window.addEventListener('resize', () => {
          //   document.getElementById('record-canvas').remove();
          //   drawVideo(video);
          // })
        } catch (err) {
          if (recorder) {
            recorder.stream.getTracks().forEach((track) => track.stop());
            recorder = null;
          }
          console.log(err.name + ' => ' + err.message);
        }
      }

      // 设置视频时长
      async function setVideoDuration() {
        const video2 = this;
        if (video2.duration !== Infinity) {
          video2.currentTime = 0;
          // console.log("video2.duration => ", video2.duration)
          videoDuration = video2.duration;
        }
      }

      // 下载录屏
      async function downloadVideo() {
        if (chunks.length === 0) throw new Error('录屏内容为空,请先录制屏幕共享内容');

        console.log('正在解析视频中,马上开始下载录屏');

        // 写入总时长
        const blob = new Blob(chunks, { type: 'video/webm' });
        const buffer = await blob.arrayBuffer();
        const newBuffer = setWebmDuration(buffer, videoDuration * 1000);
        const newBlob = new Blob([newBuffer]);
        const url = URL.createObjectURL(newBlob);

        const a = document.createElement('a');
        a.href = url;
        a.download = 'video.webm';
        a.click();
        URL.revokeObjectURL(url);
      }

      // 绘制录屏内容
      function drawVideo(video) {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        const width = video.offsetWidth || video.style.width || video.width,
          height = video.offsetHeight || video.style.height || video.height;

        canvas.id = 'record-canvas';
        canvas.width = width * devicePixelRatio;
        canvas.height = height * devicePixelRatio;
        canvas.style.width = width + 'px';
        canvas.style.height = height + 'px';

        document.body.appendChild(canvas);

        requestAnimationFrame(function _draw() {
          ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
          requestAnimationFrame(_draw);
        });
      }
    </script>
  </body>
</html>

标签:视频,const,前端,录屏,video,hidden,true,recorder
From: https://www.cnblogs.com/chlai/p/18132305

相关文章

  • 火狐浏览器看视频全屏时会黑一下屏幕
    火狐浏览器看视频全屏时会黑一下屏幕在浏览器地址栏输入:“about:config”(不包含引号,下同)并回车,然后点击“我知道了”,可以进入高级设置界面。在地址栏下方的搜索栏中输入:“full-screen-api.transition-duration”,首选项中出现两项设置,分别双击并把数值修改为“00”(注意中......
  • H5播放m3u8视频
    最近用爬虫爬取视频文件的时候,遇到了m3u8文件,按照正常的直接爬取mp4文件方式来直接爬取,只爬取了一个很小文件2K左右,我就觉得很奇怪,最后打开了文件看了下:还要将一段段的ts文件爬下来才可以正常的用h5来播放。最后爬取下来了m3u8文件和对应的ts文件,这时要用h5播放了,如果直......
  • 前端使用 Konva 实现可视化设计器(4)
    给上一章做一个补充,就是实现通过上下左右按键移动所选节点。继续求Star,希望大家多多一键三连,十分感谢大家的支持~创作不易,Star50个,创作加速!github源码gitee源码示例地址通过按键移动节点准备工作给SelectionTool添加两个必要的方法://更新已选位置selecti......
  • pdf在前端网页的显示
    背景:react框架实现插件:pdfjs-dist:"^2.0.943"  (还有其他插件  react-pdf, react-pdf-js,pdf.js都可以尝试一下呢)实现效果:实现代码如下:index.jsximportReact,{useState,useEffect}from'react';import{Spin}from'antd';import{RightOutlined,L......
  • 前端纯原生js数据监控,更新视图
    还没完善好,先记录一部分 binding.js//定义Model类,用于存储数据和监听数据变化functionModel(value=""){this._value=value;//存储数据的值this._listeners=[];//存储监听数据变化的函数}//定义Model类的set方法,用于设置数据的值Model.prototype.set......
  • 智慧矿山视频智能监控与安全监管方案
    一、行业背景随着全球能源需求的日益增长,矿业行业作为国民经济的重要支柱,其发展日益受到广泛关注。然而,传统矿山管理模式的局限性逐渐显现,如生产安全、人员监管、风险预警等方面的问题日益突出。因此,智慧矿山智能监管方案的提出,旨在通过集成先进的信息技术,实时监测矿山生产过程中......
  • 手工店/陶艺连锁店视频智能监控监管方案设计与建设
    随着科技的发展,智能监控方案在零售行业中越来越受到重视。对于陶艺连锁店来说,建设一个高效的视频监控系统能够帮助管理人员及时监控店内情况,提高安全性并优化运营管理。TSINGSEE青犀陶艺连锁店视频智能监控方案旨在通过先进的视频技术和人工智能技术,为陶艺连锁店提供全面、高效、......
  • electron集成第三方视频会议(整个目录资源含exe)进来,开发/打包坑点集锦
    场景:electron做个welink那种会议功能,需要集成第三方去进入会议,需要做的是在electron里面打开这个通道对方给了一个文件夹,里面含有.exe,需要调用shell命令去打开这个exe传些参数1.把整个会议文件夹放在/resources下主要是记住三个环境变量的路径方法(因为在electron中所以得看el......
  • 抖音评论采集|视频评论批量提取下载工具
    大家好,随着抖音平台的蓬勃发展,现在视频评论都已成为了个人和企业拓展客户群体、了解用户喜好的重要途径之一。现在为了帮助您更高效地采集并分析抖音视频评论,推出了全新版本的抖音视频评论批量采集工具,为您的营销策略提供强有力的支持!......
  • 文献学习-33-一个用于生成手术视频摘要的python库
    VideoSum:APythonLibraryforSurgicalVideoSummarizationAuthors: LuisC.Garcia-Peraza-Herrera,SebastienOurselin,andTomVercauterenSource: https://arxiv.org/pdf/2303.10173.pdf这篇文章主要关注的是如何通过视频摘要来简化和可视化手术视频,以便于数......