精华内容
下载资源
问答
  • 前端也能实现录屏

    千次阅读 2019-04-24 17:55:00
    Chrome录屏插件 准备: chrome plugin: 关于录屏功能的开发可以看下面这篇文章引导你的思路(技术不用看只是理思路): https://www.jianshu.com/p/97ecb4a6e4ce ...

    Chrome录屏插件

    准备:
    chrome plugin:
    关于录屏功能的开发可以看下面这篇文章引导你的思路(技术不用看只是理思路):
     
    看完后,下面是chrome插件开发的共享屏幕demo,你可以直接拿来用:
     
    下载完后打开扩展程序,按图操作:
     
    1. 勾选开发者模式会看见下面有一行按钮

     

    1. 点击打包扩展程序会弹出模态框,找到你extension文件点击确定,打包扩展程序

    extension:demo插件

     

    1. 打包后就会提示你打包成功,直接点击确定就行别的先不管,然后你会看到如下图所示一般

    默认的都是已启用状态,你需要勾选‘允许访问文件网址’(重点):

     

     
    1. 最后打开index.html文件测试是否成功。
    录屏demo:
    这是录屏功能demo:
     
    你需要下载:
     
    下载完后,把 MediaStreamRecorder-master / demos / video-recorder.html 拿出来,
    录屏有它就够了,运行之前将下载的补丁js跟它放到同级目下。
     
    chrome plugin 与 录屏功能整合:
     
    打开 video-recorder.html后,你会发现它是调用摄像头录像的功能,那么我们只需要将它这个功能改成我们之前做好的捕捉屏幕的功能就好了。具体如下:
     
    1. 首先打开video-recorder.html,里面有许多功能按钮,例:start,stop,save等

    功能虽然多本质都是一样的,将这三个功能函数直接copy出来放到我们的app.js中,

    如下图:

     

    注意:合并虽然简单但又很麻烦要考虑许多情况,有时候改着改着就乱了。在这里我就举个简单例子得了,比如:将chrome plugin 中的 app.js的捕捉屏幕功能直接换成 start功能。

     
    特别注意:需要将onMediaSuccess函数copy出来,这个是录屏功能的关键函数。
     
    说明:
    onMediaSuccess中你会看到有timeInterval的变量,首先它是以毫秒为单位,是MediaRecorder.start的参数:
     
    MediaRecorder.start(timeslice)
    timeslice Optional
    The number of milliseconds to record into each Blob. If this parameter isn't included, the entire media duration is recorded into a single Blob unless the requestData() method is called to obtain the Blob and trigger the creation of a new Blob into which the media continues to be recorded.
    利弊:
    它越大在切屏时的录屏效果也会非常好,但是录制的时间比你实际录制的时间会短
    当它调小的时候就不说了切屏时,画面浑浊并停止录屏。
    所以一般都是大点,设置成五秒以上就可以了。
     
    最后你可以打开html看到你的成果啦!

    转载于:https://www.cnblogs.com/listeners/p/10763862.html

    展开全文
  • 使用minicap+python实现录屏功能

    千次阅读 2020-02-26 12:15:38
    一:minicap简介: minicap属于STF框架的一个工具,可以高速截图、同步手机屏幕至浏览器等功能,经过...起到至关重要的作用,假如你要做一个类似于腾讯的wetest网测平台,那么或许你可以尝试一下结合minicap工具实现...

    一:minicap简介:
    minicap属于STF框架的一个工具,可以高速截图、同步手机屏幕至浏览器等功能,经过试验,截同一个屏幕的一张图,使用adb shell screencap命令进行截图速度为2.9秒,而minicap仅为0.8秒,效率高很多,这在基于图像识别的自动化测试中,起到至关重要的作用,假如你要做一个类似于腾讯的wetest网测平台,那么或许你可以尝试一下结合minicap工具实现屏幕同步功能。

    二:minicap获取途径:
    1,通过下载源码进行编译,源码地址:https://github.com/openstf/minicap,编译环境为NDK+make,有兴趣的朋友可以参考https://www.cnblogs.com/qiangayz/p/9580389.html进行尝试,但本人觉得windows的环境设置有点麻烦,就选择了其他现成的minicap进行操作。,
    2,airtest源码项目进行minicap获取,airtest是网易开发的一款开源项目, 它里面集成了minicap和minitouch等工具,使用这些工具我们只需要到相应的目录去查找即可,下面介绍通过使用airtest上的minicap实现录屏功能。

    三:将airtest上的minicap及minicap.so文件push到手机固定的目录当中去。
    1,下载Airtest IDE,官方网站http://airtest.netease.com/,解压后获得项目源码。
    在这里插入图片描述
    2,使用pycharm打开项目,进入AirtestIDE_2020-01-21_py3_win64\airtest\core\android\static\stf_libs,如图可以看到,stf_libs目录下有着各种minicap版本的父目录。它是根据手机CPU架构(arm64-v8a、armeabi-v7a,x86,x86_64)和sdk版本进行分类的。
    在这里插入图片描述
    3,获取手机cpu架构,sdk版本
    cpu版本:adb shell getprop ro.product.cpu.abi
    sdk版本:adb sehll getprop ro.build.version.sdk
    如图,可以看到cpu的架构是arm64-v8a,sdk版本是26
    在这里插入图片描述
    4,在airtest上找到相应的minicap可执行文件和minicap.so文件。minicap在(AirtestIDE_2020-01-21_py3_win64\airtest\core\android\static\stf_libs\arm64-v8a)目录下,而minicap.so在(airtest\core\android\static\stf_libs\minicap-shared\aosp\libs\android-27\arm64-v8a)目录下。
    在这里插入图片描述
    在这里插入图片描述
    5,push minicap,minicap.so到手机中
    adb push minicap /data/local/tmp
    adb push minicap.so /data/local/tmp

    6,对它们进行赋权
    adb shell chmod 777 /data/local/tmp/minicap
    adb shell chmod 777 /data/local/tmp/minicap.so

    四:获取手机分辨率后测试并且启动minicap服务

    1,屏幕分辨率:adb shell wm size
    在这里插入图片描述
    2,测试是否可运行:adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x2160@1080x2160/0 -t
    在这里插入图片描述
    3,启动minicap服务:adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 1080x2160@1080x2160/0
    -P 后面的参数格式:{RealWidth}x{RealHeight}@{VirtualWidth}x{VirtualHeight}/{Orientation}
    Orientation可以理解为手机的旋转角度,可选参数为 0 | 90 | 180 | 270

    五. 端口映射,把minicap映射到1717端口,也可以是其他端口。**
    1,adb forward tcp:1717 localabstract:minicap
    在这里插入图片描述

    六,进行测试
    1,minicap源码里面有example/文件夹下就是一个minicap提供的测试服务,由node.js搭建的服务端
    所以启动需要本地有node.js环境,具体实现大概为开启socket连接,监听1717端口,然后再开一个websocket把监听到数据发给前端页面启动方式为:node app.js PORD=9002
    pord为指定前端页面端口,
    2,打开浏览器访问:http://localhost:9002/ 即可看到手机投屏
    在这里插入图片描述
    七:通过python实现录屏:
    1,执行下面代码,可以看到在当前目录下会有手机截屏图片生成。

    # coding: utf8
    import socket
    import sys
    import time
    import struct
    from collections import OrderedDict
    
    
    class Banner:
        def __init__(self):
            self.__banner = OrderedDict(
                [('version', 0),
                 ('length', 0),
                 ('pid', 0),
                 ('realWidth', 0),
                 ('realHeight', 0),
                 ('virtualWidth', 0),
                 ('virtualHeight', 0),
                 ('orientation', 0),
                 ('quirks', 0)
                 ])
    
        def __setitem__(self, key, value):
            self.__banner[key] = value
    
        def __getitem__(self, key):
            return self.__banner[key]
    
        def keys(self):
            return self.__banner.keys()
    
        def __str__(self):
            return str(self.__banner)
    
    class Minicap(object):
        def __init__(self, host, port, banner):
            self.buffer_size = 4096
            self.host = host
            self.port = port
            self.banner = banner
    
        def connect(self):
            try:
                self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            except :
                print('2')
                sys.exit(1)
            self.__socket.connect((self.host, self.port))
    
        def on_image_transfered(self, data):
            file_name = str(time.time()) + '.jpg'
            with open(file_name, 'wb') as f:
                for b in data:
                    f.write(b)
    
        def consume(self):
            readBannerBytes = 0
            bannerLength = 24
            readFrameBytes = 0
            frameBodyLength = 0
            data = []
            while True:
                try:
                    chunk = self.__socket.recv(self.buffer_size)
                except:
                    print('11')
                    sys.exit(1)
                cursor = 0
                buf_len = len(chunk)
                while cursor < buf_len:
                    if readBannerBytes < bannerLength:
                        map(lambda i, val: self.banner.__setitem__(self.banner.keys()[i], val),
                            [i for i in range(len(self.banner.keys()))], struct.unpack("<2b5ibB", chunk))
                        cursor = buf_len
                        readBannerBytes = bannerLength
                        print(self.banner)
                    elif readFrameBytes < 4:
                        frameBodyLength += (struct.unpack('B', chunk[cursor])[0] << (readFrameBytes * 8)) >> 0
                        cursor += 1
                        readFrameBytes += 1
                    else:
                        print("frame length:{0} buf_len:{1} cursor:{2}".format(frameBodyLength, buf_len, cursor))
                        # pic end
                        if buf_len - cursor >= frameBodyLength:
                            data.extend(chunk[cursor:cursor + frameBodyLength])
                            self.on_image_transfered(data)
                            cursor += frameBodyLength
                            frameBodyLength = readFrameBytes = 0
                            data = []
                        else:
                            data.extend(chunk[cursor:buf_len])
                            frameBodyLength -= buf_len - cursor
                            readFrameBytes += buf_len - cursor
                            cursor = buf_len
    
    if '__main__' == __name__:
        print('开始')
        mc = Minicap('localhost', 1717, Banner())
        mc.connect()
        mc.consume()
       
    

    2,将图片帧转换为视频,执行下面代码:

    import cv2
    import glob
    import os
    from datetime import datetime
    
    
    def frames_to_video(fps, save_path, frames_path, max_index):
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        videoWriter = cv2.VideoWriter(save_path, fourcc, fps, (960, 544))
        imgs = glob.glob(frames_path + "/*.jpg")
        frames_num = len(imgs)
        for i in range(max_index):
            if os.path.isfile("%s/%d.jpg" % (frames_path, i)):
                frame = cv2.imread("%s/%d.jpg" % (frames_path, i))
                videoWriter.write(frame)
        videoWriter.release()
        return
    
    
    if __name__ == '__main__':
        t1 = datetime.now()
        frames_to_video(22, "result.mp4", 'face_recog_frames', 1000)
        t2 = datetime.now()
        print("耗时 :", (t2 - t1))
        print("完成了 !!!")
    
    

    3,结果,可以看到当前项目目录有一个result.mp4生成,耗时也是相当的短,可以看到通过minicap进行截图效率大大提升了。
    在这里插入图片描述
    在这里插入图片描述

    八:总结。
    1,在启动minicap过程中,出现没有权限的问题,安卓手机在没有root的情况下是没有权限对系统文件进行读写操作的,但是可通过adb命令进行权限赋予。adb shell chmod 777
    在这里插入图片描述
    2,一个简单的基于minicap的录屏功能就基本完成了,后续将基于minicap进行实时录屏操作,并加入minitouch进行点击操作,期待能实现更多更多的功能!

    展开全文
  • 前段时间做一个windows的桌面应用,vue + electron,涉及到录屏和摄像功能,网上相关的文档蛮少的给需要的人一些参考 如果文章描写有误或者还有更好的方法,请留言告诉我,笔芯 (´▽`ʃ♡ƪ) 背景介绍 ** 涉及...

    前段时间做一个windows的桌面应用,vue + electron,涉及到录屏和摄像功能,网上相关的文档蛮少的给需要的人一些参考

    如果文章描写有误或者还有更好的方法,请留言告诉我,笔芯 (´▽`ʃ♡ƪ)

    背景介绍

    ** 涉及技术:vue、electron、ffmpeg、node **

    关于录屏和摄像对比了两种方法

    • 使用HTML5的api实现

      摄像:mediaDevices(获取设备)+ getUserMedia(获取流) + MediaRecorder(存储)

      录屏:getDisplayMedia(获取流) + MediaRecorder(存储)

    • ffmpeg + node:FFmpeg是一套非常强大的音视频处理的开源工具,不多介绍,而Electron基于node和chromium,它允许使用node的API以及几乎所有的node模块,这意味这着我们可以调用cmd命令来操作ffmpeg实现录屏和摄像录制,当然ffmpeg功能绝不止这点

    HTML5实现

    mediaDevices

    • 用于收集系统上可用的多媒体输入和输出设备的信息

    • 该方法调用成功返回设备列表,并传入带有devceID的MediaStreamConstraints对象可以指定设备获取流媒体来源

      navigator.mediaDevices.enumerateDevices().then(devicelist => {
        // audiooutput 扬声器
        // audioinput  麦克风
        // audiooutput 摄像
        console.log(devicelist)
      }).catch(err => console.log(err))

    getUserMedia

    • 用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。

    • 该方法返回视频流,将获取到的流赋给video标签可实现边录边看

    navigator.mediaDevices.getUserMedia(MediaStreamConstraints).then(stream => {
        videoElement.srcObject = stream; // 
      }, error => console.log(error));
    
    
    getDisplayMedia
    
    • 将用户的显示或其部分用作媒体流的来源,它允许以视频流的形式获取用户的显示器或其一部分

    • 录屏主要依靠该方法,和getUserMedia一样返回一个promise对象,调用成功返回流,将这个流赋给video 元素实现边录边看

    • ** 需要注意的是** 如果你做的是网页端在谷歌上使用这个元素,需要在chrome://flags/开启Experimental Web Platform features功能

        而Electron基于node + chromium构建,在electron需要引入desktopCapturer模块,并在基于该模块使用这个方法

     
    navigator.mediaDevices.getDisplayMedia({ video: true })
      .then(stream => {
        videoElement.srcObject = stream;
      }, error => console.log(error));

    MediaRecorder

    • 记录和捕获媒体,也就是视频和音频

    • getDisplayMedia 和 getUserMedia 获取到的流都需要使用MediaRecorder存储起来,并且可以保存成文件

    let herf
    this.recorder = new MediaRecorder(stream);
    this.recorder.ondataavailable = e => { 
      herf = e.data;
      download.href = URL.createObjectURL(herf);
    };
    this.recorder.start();

    第二种使用ffmpeg

    官网安装包下载 https://ffmpeg.zeranoe.com/builds/

    一些基本参数

    • -formats 输出所有可用格式
    • -f fmt 指定格式(音频或视频格式)
    • -i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头
    • -y 覆盖已有文件
    • -t duration 记录时长为t
    • -fs limit_size 设置文件大小上限
    • -itsoffset time_off 设置时间偏移(s),该选项影响所有后面的输入文件。该偏移被加到输入文件的时戳,定义一个正偏移意味着相应的流被延迟了 offset秒。 [-]hh:mm:ss* [.xxx]的格式也支持
      音 频
    • -ab bitrate 设置音频码率
    • -ar freq 设置音频采样率
    • -ac channels 设置通道 缺省为1
      视 频
    • -b bitrate 设置比特率,缺省200kb/s
    • -r fps 设置帧频 缺省25
    • -s size 设置帧大小 格式为WXH 缺省160X128.下面的简写也可以直接使用:

    录屏相关命令

     
    列出可用的设备包括音频和摄像等等
    ffmpeg -list_devices true -f dshow -i dummy
     
    录屏,你也可以加入关于视频的一些基本参数来获得你想要的文件
    ffmpeg -f gdigrab -i desktop captrue.mkv -y

    node调用

    cd进入bin文件夹后执行录屏相关命令

    关于停止录制,虽然ffmpeg按 Q 可以停止录制,但是我们通过代码调用是看不到cmd命令行的而且他在录制过程中是一直占用这个进程什么命令也无法输入
    所以这个地方我只想到一个办法就是强制停止该进程

    参考文章

    MDN https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
    https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
    https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/enumerateDevices

    W3C https://w3c.github.io/mediacapture-screen-share/

    转载于:https://blog.csdn.net/xuehu837769474/article/details/89089333 

    展开全文
  • import Vue from "vue"; import VueRouter, { RouteConfig } from "vue-router"; import Main from "../views/Main.vue"; Vue.use(VueRouter); const routes: RouteConfig[] = [ { path: "/", ...
  • JS录屏以及录屏视频下载功能实现,可选择整个屏幕、程序窗口、浏览器页面 首先看一下功能实现后的效果图,因为上传图片的大小限制,效果图使用的480分辨率视频转的gif,所以比较模糊,实际录制可以根据需求调整视频...

    JS录屏以及录屏视频下载功能实现,可选择整个屏幕、程序窗口、浏览器页面

    首先看一下功能实现后的效果图,因为上传图片的大小限制,效果图使用的480分辨率视频转的gif,所以比较模糊,实际录制可以根据需求调整视频分辨率。

    在这里插入图片描述
    在进行录制之前需要先给IP设置权限,否则浏览器出于隐私保护不允许进行录制,下面是谷歌浏览器设置权限示例,实际开发请调整。
    在谷歌浏览器中打开该网址:chrome://flags/#unsafely-treat-insecure-origin-as-secure
    将自己的IP地址复制到文本框中,勾选Enabled后重启浏览器
    在这里插入图片描述
    该功能目前仅可以实现视屏轨的获取,音频轨尚无法实现。
    1. 创建媒体数据流

    在进行录制时,首先要确定媒体数据流,该媒体数据流必须以video形式或者audio形式,前者为视屏,后者为音频。例如我们的显示屏窗口,前后摄像头都可以作为video。

    async record() {
    	let captureStream;
    	var constraints = { video:{ width: 720, height: 720 } };
    	try{
        	captureStream = await navigator.mediaDevices.getDisplayMedia(constraints)//getUserMedia()
        }catch(e){
            alert("录屏已取消或者未设置权限");
            return
        }
    }
    

    constraints为要创建媒体数据流的形式,video表示我要获取一个分辨率为720的视频轨(一个媒体数据流可以有多个数据轨)。调用浏览器对象navigator中的mediaDevices接口下getDisplayMedia方法获取到媒体数据流赋值给captureStream ,这里要用异步执行确保后续代码执行时已经获取到媒体数据流。MediaDevices 接口提供访问连接媒体输入的设备,如照相机和麦克风,以及屏幕共享等。它可以使你取得任何硬件资源的媒体数据。这里为共享屏幕,浏览器会弹出选项询问用户要录制的窗口。

    2. 创建一个MediaRecorder对象控制媒体数据流的存储

    this.recorder = new MediaRecorder(captureStream);
    this.recorder.start();
    this.recorder.stop();
    

    MediaRecorder对象可以对媒体数据流进行操作,如start()为开始录制,stop()为停止录制。还有暂停、删除等等,详情可以去WebAPI中查看,文章结尾我会给出链接。这里的开始录制和停止录制并非是视频录制的开始和停止,而是指媒体数据流什么时候开始存储和停止存储,看个人的理解。当执行一次开始和结束后,就获取到了一段媒体数据流。

    3. dataavailable对媒体数据流进行操作
    dataavailable为事件的事件处理程序,只要有可用数据,就会调用此方法。

     this.recorder.addEventListener("dataavailable", event => {
    	console.log('数据流为blob对象且可用',event.data);
     })
    

    监听MediaRecorder对象的dataavailable,dataavailable会去监听stop(),也就是当stop()事件发生时且有一段媒体数据流被获取到,dataavailable就会执行,参数event,媒体数据流存储在event.data下。如果没有媒体数据流则不执行。

    4. 实时播放录屏

    <video  autoplay='true' id='video' controls='true' controlsList='nodownload'></video>
    
    this.video = document.getElementById('video');
    
    this.video.srcObject = captureStream;
    

    通常video的播放内容由src确定,为视频的链接地址,这里要实时的去播放录屏,需要使用srcObject,srcObject 属性设定或返回一个对象,这个对象提供了一个与媒体接口关联的媒体源,通常这个对象通常是 MediaStream,captureStream创建媒体数据源且也与媒体接口关联,且是一个MediaStream对象,起一个类似于地址的作用使video播放实时录屏的内容。

    5. 停止实时播放录屏
    这里为什么要把停止实时的录屏的播放摘出来说一呢,因为当我们实时播放的时候,点击video上暂停按钮只是当前的画面停止而已,并不是真正意义上的停止录屏,而且当我们只是对媒体数据执行stop()时,也只是停止了媒体数据的获取,不能够停止实时播放录屏。

    let tracks = this.video.srcObject.getTracks();
    tracks.forEach(track => track.stop());
    this.recorder.stop();
    

    getTracks()会返回媒体数据流中的所有轨道,前面提到过,一个媒体数据流包含多个媒体数据,如视频轨、音频轨等等。通过forEach对每一个媒体数据轨进行stop(),这样video才是正正意义上的停止了实时的录屏播放而非暂停,当我们停止了实时录屏播放时,也应该停止媒体数据流的获取,实现同步。

    6. 录制视频的回放和下载

    这里就要用dataavailable对媒体数据流进行操作

    this.recorder.addEventListener("dataavailable", event => {
    	 console.log('数据流为blob对象且可用',event);
    	 let videoUrl = URL.createObjectURL(event.data, {type: 'video/mp4'});
    	 this.video.srcObject = null;
    	 this.video.src = videoUrl;
    })
    
    download(){
    	const url = this.video.src;
    	const name = new Date().toISOString();
    	const a = document.createElement('a');
    	a.style.display = 'none';
    	a.download = `${name}.mp4`;
    	a.href = url;
    	document.body.appendChild(a);
    	a.click();
    	console.log('释放 Blob');
    	window.URL.revokeObjectURL(this.video.src);
    },
    

    URL.createObjectURL创建一个url,这个url表示指定的 File 对象或 Blob 对象。video.srcObject 为null停止实时录屏播放,将新创建的对象赋值给video.src做为播放的地址实现回放。创建一个a标签来添加download插入到body中实现下载,下载的地址为video.src播放地址,名字为当前时间,a标签的样式不可见,当创建好a标签后就对a标签进行click()点击将视频下载,最后对创建的url进行释放, 使用URL.createObjectURL后必须在使用URL.revokeObjectURL进行释放,虽然浏览器在 document 卸载的时候,会自动释放url,但是为了获得最佳性能和内存使用状况,应该在安全的时机主动释放掉url。

    以上为JS实现录屏及屏视频下载功能的所有关键要点,整体代码就不在博文里放了,以下是功能实现的WebAPI参考链接,有兴趣可以看一下

    WebPAI MediaStream 实现屏幕录制

    展开全文
  • 如题,用MediaRecorder录屏,现在的需求是能够发送流媒体的同时还能保存为一份本地文件,该如何实现
  • web页面录屏实现

    2021-01-19 21:30:18
    在前面的话 在看到评论后,突然意识到自己没有提前说明,本文可以说是一篇调研学习文,是我自己感觉可行的一套方案,后续会去读读已经开源的一些...实现思路 思路一:利用Canvas截图 这个思路比较简单,就是利用can
  • Web 页面录屏实现

    2019-09-09 13:13:28
    写在前面的话 ...录屏重现错误场景 如果你的应用有接入到web apm系统中,那么你可能就知道apm系统能帮你捕获到页面发生的未捕获错误,给出错误栈,帮助你定位到BUG。但是,有些时候,当你不知道...
  • 请留言告诉我,笔芯 (´▽`ʃ♡ƪ)背景介绍** 涉及技术:vue、electron、ffmpeg、node **关于录屏和摄像对比了两种方法使用HTML5的api实现摄像:mediaDevices(获取设备)+ getUserMed...
  • button text="开始录屏"style="Widget.AppCompat.Button.Colored" id="button"/> </vertical> ); ui.button.click(function() { if (running) { stopRecord(); ui.button.setText("开始录屏"); } ...
  • 直接上源码,我的代码借鉴了这个老哥用Vue.js写的https://blog.csdn.net/blueXh/article/details/88955821 html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <...
  • 如何实现Web页面录屏

    千次阅读 2019-09-09 12:27:14
    原文:web页面录屏实现 译者:frontdog Fundebug经授权转载,版权归原作者所有。 写在前面的话 在看到评论后,突然意识到自己没有提前说明,本文可以说是一篇调研学习文,是我自己感觉可行的一套方案,后续会去读读...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,450
精华内容 580
关键字:

js实现录屏