前端如何录制屏幕

需求

最近需要做的一个功能就是:通过你的网页应用上的一个按钮可以录制当前的浏览器屏幕,保存成视频文件,并上传。这个需求的难点不在于文件的上传,而是如何去录制浏览器的屏幕,找了大量的资料,回答的都是这么调用摄像头和麦克风去录屏,这个录屏录得就不是web应用的内容了。

在做了很多探索以后,目前唯一可行的方案如下:(如果有别的方案欢迎交流)

接口

目前找到的接口是chrome的接口chrome.desktopCapture,对于这个接口的描述是:它能用来捕获屏幕的内容, 包含了windows窗口或者tabs。

捕获屏幕的信息对于用户来说比较隐私,有很大的安全方面的考虑,所以无法mediaDevices接口获取屏幕。这个设计也合理,如果开发了这个权限,如果一些网站嵌入了捕获屏幕的脚本,那么用户的上网隐私就泄露了。

那对于我们确实需要这个功能的时候,可以怎么去实现呢?
对于Chrome浏览器,我们可以写一个extension应用,或者叫插件,通过这个extension来让我有权限获取浏览器屏幕。

extension

创建一个extension很简单,只需要两个文件:manifest.json和extension.js。
1.创建一个文件夹,放这两个文件
2.manifest.json中放置的是对于extension的一些描述性信息,里面会指定脚本为extension.js,并且带有权限信息。

{
  "name": "Desktop Capture",
  "description": "Allows you to capture your desktop for use in video applications",
  "version": "0.1.0",
  "manifest_version": 2,
  "background": {
    "scripts": ["extension.js"],
    "persistent": false
  },
  "externally_connectable": {
    "matches": [
      "*://localhost/*"
    ]
  },
  "permissions": ["desktopCapture"],
  "key": ""
}

一般的extension可以不用指定key,在上传extension到chrome上时会自动配置。点击下图的pack extension可以打包extension,会自动补上pem文件包含private key。


image.png

我的extension中指定了key,因为我的extension没有上传到chrome的extension商店中,但是在各个浏览器安装时,生产的key必须是一致的,所以就指定了key值。

3.extension.js中是脚本,脚本中创建一个事件监听,接受从web APP发来的消息,通过chrome.desktopCapture.chooseDesktopMedia弹出选择屏幕选择框,包含“screen”、“window”、 “tab”和 “audio”。

chrome.runtime.onMessageExternal.addListener(
  (message, sender, sendResponse) => {
    if (message == 'version') {
      sendResponse({
        type: 'success',
        version: '0.1.0'
      });
      return true;
    }
    const sources = message.sources;
    const tab = sender.tab;
    chrome.desktopCapture.chooseDesktopMedia(sources, tab, streamId => {
      if (!streamId) {
        sendResponse({
          type: 'error',
          message: 'Failed to get stream ID'
        });
      } else {
        sendResponse({
          type: 'success',
          streamId: streamId
        });
      }
    });
    return true;
  }
);

上传extension

打开chrome://extensions/
点击打开developer mode,然后点击Load unpacked,上传我们的extension文件夹。会看到下图一样的extension。

image.png

调用extension录屏

这个是angular版的代码,创建了一个插件。点击开始,开始录制,点击结束,生成文件下载到本地。

<button *ngIf="!recording" (click)="startCaptureScreen()">开始录制</button>
<button *ngIf="recording" (click)="stopCaptureScreen()">结束录制</button>

import { Component, OnInit } from '@angular/core';

declare var MediaRecorder: any;
declare var chrome: any;

@Component({
  selector: 'app-screen-capture',
  templateUrl: './screen-capture.component.html',
  styleUrls: ['./screen-capture.component.scss']
})
export class ScreenCaptureComponent implements OnInit {
  public recording = false;
  public recordedBlobs = [];
  public EXTENSION_ID = 'XXXXXXX';

  public mediaRecorder;
  public stream;

  constructor() {}

  ngOnInit() {}

  download() {
    const blob = new Blob(this.recordedBlobs, { type: 'video/mp4' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.style.display = 'none';
    a.href = url;
    a.download = 'test.mp4';
    a.click();
    window.URL.revokeObjectURL(url);
  }

  stopCaptureScreen() {
    this.stream.getTracks().forEach(track => track.stop());
    this.recording = false;
    this.mediaRecorder.stop();
  }

  startCaptureScreen() {
    this.recording = true;
    const request = { sources: ['window', 'tab'] };
    chrome.runtime.sendMessage(this.EXTENSION_ID, request, response => {
      if (!response) {
        console.log('No extension');
        return;
      }
      if (response && response.type === 'success') {
        navigator.mediaDevices
          .getUserMedia({
            video: {
              mandatory: {
                chromeMediaSource: 'desktop',
                chromeMediaSourceId: response.streamId
              }
            } as MediaTrackConstraints
          })
          .then(returnedStream => {
            this.stream = returnedStream;
            const options = {
              audioBitsPerSecond: 128000,
              videoBitsPerSecond: 2500000,
              mimeType: 'video/webm'
            };
            this.mediaRecorder = new MediaRecorder(this.stream, options);

            this.mediaRecorder.ondataavailable = event => {
              if (event.data.size > 0) {
                this.recordedBlobs.push(event.data);
                this.download();
              }
            };
            this.mediaRecorder.start();
          })
          .catch(err => {
            console.error('Could not get stream: ', err);
          });
      } else {
        console.error('Could not get stream');
      }
    });
  }
}

参考文章:
Screen capture in Google Chrome
Chrome Extension 開發與實作 24-打造螢幕錄影功能 chrome.desktopCapture

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生x阅读 15,967评论 3 119
  • 无论是小礼服还是晚礼服,只要跟这个“礼”沾边,价格都不便宜,所以自然心疼爱惜,要好好保护起来。 慵懒收纳术第三十九...
    收了纳个Queen阅读 1,952评论 0 2
  • 没有经历过什么大的演讲,回顾自己几次上台的经历,总是差强人意。把文中介绍的步骤套用下来反思。问题主要存在主菜和甜点...
    我是珊珊呀阅读 177评论 1 1
  • 六六裂变增长实验室挑战第二天:学习了,海报的制作,及文案的思路,结合第一天的实操项目一起做了实操 第一次知道 墨刀...
    煜昊阅读 252评论 0 0
  • 01. 找闺密聊天,一打就是175分钟。美其名曰:谈人生谈理想。 我和她性格相似,很多观点不谋而合。于结婚这件事的...
    鸭蛋00阅读 275评论 1 1