编程 用Electron + PixiJS从零打造开源录屏工具:OpenScreen的采集管线、渲染架构与导出优化

2026-04-17 17:49:52 +0800 CST views 3

OpenScreen 深度解析:GitHub 2万+ Stars 的开源录屏神器,如何用 Electron + PixiJS 重塑开发者内容创作体验

前言

做技术分享最痛苦的事情是什么?

不是写代码,不是调试 bug,而是——录一个体面的产品演示视频。

你吭哧吭哧录完 30 分钟的 Demo,导出后视频角落那个去不掉的水印像个幽灵一样提醒你:"免费版到此为止。"你咬牙花了 299 买了个 Screen Studio,结果发现导出还得排队、云同步还收费、Mac 上跑倒是流畅,Windows 客户打开一看——卡成 PPT。

2026 年 4 月,一款名为 OpenScreen 的开源录屏工具在 GitHub 悄然走红,上线不到两周狂揽 22,000+ Stars,连续多日登顶 GitHub Trending 榜首,成为开发者社区讨论热度最高的新晋明星项目。它号称"Screen Studio 的零成本替代方案"——完全免费、无订阅、无水印、MIT 协议可商用。

但等等,一款录屏软件凭什么这么火?Electron + React + PixiJS 的技术栈,听起来也就是个"中规中矩"的全栈组合,凭什么能撬动 Screen Studio 这种商业软件的墙角?

带着这个疑问,我花了一整周时间深入研究了这个项目,从源码架构到渲染管线、从产品设计哲学到开发者生态,把 OpenScreen 的"底裤"扒了个干净。这篇文章,就是我这次研究的完整输出。


一、背景:开发者内容创作的"最后一公里"困境

1.1 录屏工具的现状

在正式解析 OpenScreen 之前,我们需要先理解一个背景:为什么开发者社区对"好的录屏工具"有如此强烈的需求,但市面上却长期找不到满意的产品?

让我梳理一下当前的录屏工具生态:

专业级(影视制作):DaVinci Resolve、Camtasia、OBS Studio。这类产品功能强大到可以拍电影,但学习曲线陡峭,导出的视频体积巨大,不适合快速做技术演示。

商业 SaaS(演示工具):Screen Studio(129 美元/年)、Loom(免费版有限制)、Camtasia(249 美元一次性买断)。这类产品定位精准,就是给做产品演示的人用的,但普遍存在:

  • 免费版水印 / 功能残缺
  • 订阅制年年涨价
  • 跨平台体验不一致(Screen Studio 只有 macOS)
  • 导出格式单一,不方便二次编辑

系统自带:macOS QuickTime、Windows Xbox Game Bar。前者只能录系统声、不能录麦克风、导出格式老旧;后者本质是游戏录制工具,产品演示场景下 UI 极度不友好。

开源生态:几乎空白。OBS Studio 是开源的,但它本质是个直播/录制工具,不是"演示创作工具"——你需要自己剪辑、加字幕、调速度。

这种生态格局导致了一个有趣的现象:开发者宁愿用 Keynote 录屏,也不愿意用专业录屏软件。因为 Keynote 至少能保证"录制 + 简单编辑 + 导出"一体化,虽然导出质量堪忧。

1.2 OpenScreen 的入场逻辑

OpenScreen 的创始人 siddharthvaddem 在项目 README 中写道:

"我需要一款工具,让我能在 5 分钟内录完一个功能演示,导出后直接发到 Twitter/X 上。Screen Studio 做到了,但它收费。我做了一个开源版本。"

这个动机非常朴实,但背后折射的产品哲学很清晰:

  1. 录制即创作:不是"录完再剪辑",而是"边录边编排"
  2. 零摩擦导出:一键导出 MP4/GIF,文件体积小,社交平台友好
  3. 开发者友好:MIT 协议,源码开放,可以魔改,可以集成到 CI/CD 流程
  4. 跨平台:Electron 一次开发,Windows/macOS/Linux 全部覆盖

二、架构解析:从 Electron 到 PixiJS 的全链路技术栈

2.1 整体架构

OpenScreen 的技术栈选择非常务实,不追求"最新最潮",而是追求"够用且稳定"。核心依赖如下:

层级技术选型选型理由
跨平台运行时Electron 32.x成熟稳定,Web 技术栈可复用
前端框架React 18 + TypeScript组件化,生态完善
状态管理Zustand轻量、无样板代码
图形渲染PixiJS 8.xWebGL 加速,2D 渲染性能极佳
音视频处理Web Audio API + MediaRecorder平台原生 API,无需 FFmpeg 依赖
构建工具Vite + electron-builder开发体验好,打包体积可控
IPC 通信Electron IPC (contextBridge)安全隔离主进程与渲染进程

整体架构可以用一句话概括:Electron 提供跨平台窗口和系统级 API,React 构建 UI,PixiJS 负责录屏画面的实时渲染和动画效果。

2.2 主进程与渲染进程分离

OpenScreen 遵循 Electron 最佳实践,将应用分为主进程(Main Process)渲染进程(Renderer Process)

┌─────────────────────────────────────────────┐
│              Main Process (Electron)          │
│                                              │
│  ┌──────────────┐  ┌──────────────────────┐ │
│  │ WindowManager │  │  ScreenCaptureService │ │
│  │              │  │  - getUserMedia()     │ │
│  │  - 创建窗口   │  │  - DisplayAPI         │ │
│  │  - 菜单栏    │  │  - 音频采集            │ │
│  │  - 系统托盘   │  │  - 帧缓冲管理          │ │
│  └──────────────┘  └──────────────────────┘ │
│                                              │
│  ┌──────────────┐  ┌──────────────────────┐ │
│  │   IPC Bridge │  │     FileService       │ │
│  │              │  │  - 导出 MP4           │ │
│  │  安全双向通信 │  │  - 导出 GIF           │ │
│  └──────────────┘  └──────────────────────┘ │
└──────────────────┬──────────────────────────┘
                    │ contextBridge (安全 IPC)
┌──────────────────┴──────────────────────────┐
│           Renderer Process (React + Vite)      │
│                                              │
│  ┌──────────────┐  ┌──────────────────────┐ │
│  │  StudioCanvas │  │    TimelineEditor     │ │
│  │   (PixiJS)   │  │                        │ │
│  │              │  │  - 片段剪辑             │ │
│  │  实时预览渲染  │  │  - 速度调节            │ │
│  │  缩放/动画效果 │  │  - 片段拼接            │ │
│  └──────────────┘  └──────────────────────┘ │
│                                              │
│  ┌──────────────┐  ┌──────────────────────┐ │
│  │   UIOverlay   │  │     ControlsPanel     │ │
│  │              │  │                        │ │
│  │  - 摄像头叠加  │  │  - 录制控制            │ │
│  │  - 字幕组件   │  │  - 效果配置            │ │
│  │  - 标注工具   │  │  - 导出选项            │ │
│  └──────────────┘  └──────────────────────┘ │
└─────────────────────────────────────────────┘

这种架构的优势在于:

  1. 主进程不关心 UI:主进程只负责系统级操作(屏幕采集、文件写入、系统通知),职责单一
  2. 渲染进程无系统权限:通过 contextBridge 暴露白名单 API,避免 XSS 攻击面
  3. PixiJS 渲染完全隔离:录屏画面的实时特效渲染在 WebGL 层面完成,不阻塞 React UI 线程

2.3 屏幕采集管线

OpenScreen 的屏幕采集是整个系统最核心的部分。让我详细解析其采集管线:

2.3.1 采集来源选择

// src/services/screenCapture.ts (伪代码)

enum CaptureSourceType {
  SCREEN = 'screen',       // 整个屏幕
  WINDOW = 'window',       // 单个窗口
  AREA = 'area'            // 自定义区域
}

interface CaptureSource {
  id: string;
  name: string;
  type: CaptureSourceType;
  thumbnail: string;
}

// 使用 Electron 的 desktopCapturer API
async function getCaptureSources(): Promise<CaptureSource[]> {
  const sources = await window.electron.desktopCapturer.getSources({
    types: ['window', 'screen'],
    thumbnailSize: { width: 320, height: 180 }
  });

  return sources.map(source => ({
    id: source.id,
    name: source.name,
    type: source.id.startsWith('screen') 
      ? CaptureSourceType.SCREEN 
      : CaptureSourceType.WINDOW,
    thumbnail: source.thumbnail.toDataURL()
  }));
}

这里有个重要的技术细节:Electron 的 desktopCapturer.getSources() 会为每个屏幕/窗口生成缩略图,OpenScreen 将这些缩略图展示给用户,让用户选择要录制的内容。这是 macOS 和 Windows 共享的 API,屏蔽了底层差异。

2.3.2 MediaRecorder 录制管线

选定采集源后,OpenScreen 使用 Web API 级别的 MediaRecorder 来处理音视频流:

// src/services/recording/RecordingEngine.ts

class RecordingEngine {
  private mediaRecorder: MediaRecorder | null = null;
  private audioContext: AudioContext | null = null;
  private recordedChunks: Blob[] = [];

  async startRecording(sourceId: string, options: RecordingOptions) {
    // 1. 获取视频流(通过 Electron IPC 转发)
    const videoStream = await navigator.mediaDevices.getUserMedia({
      video: {
        // 指定要采集的源(Electron 会注入 sourceId)
        // 注意:这里的 sourceId 格式因平台而异
        mandatory: {
          chromeMediaSource: 'desktop',
          chromeMediaSourceId: sourceId,
          minWidth: 1920,
          maxWidth: 3840,
          minHeight: 1080,
          maxHeight: 2160,
          maxFrameRate: 60
        }
      },
      audio: options.captureAudio 
        ? { 
            // 混录系统音频 + 麦克风
            // 注意:macOS 10.15+ 需要用户授权
            // 注意:Windows 上系统音频采集有平台差异
            mandatory: {
              chromeMediaSource: 'desktop',
              // 混音处理在 AudioContext 中完成
            }
          }
        : false
    });

    // 2. 配置 MediaRecorder
    // 优先使用 MediaRecorder 原生支持
    // 若平台不支持指定编码器,降级为 VP8/Opus
    const mimeType = this.getSupportedMimeType();
    
    this.mediaRecorder = new MediaRecorder(videoStream, {
      mimeType,
      videoBitsPerSecond: options.bitrate || 5_000_000, // 5Mbps
      audioBitsPerSecond: 128_000 // 128kbps
    });

    // 3. 绑定数据片段收集回调
    this.recordedChunks = [];
    this.mediaRecorder.ondataavailable = (event) => {
      if (event.data && event.data.size > 0) {
        this.recordedChunks.push(event.data);
      }
    };

    // 4. 开始录制
    this.mediaRecorder.start(100); // 每 100ms 触发 ondataavailable
  }

  // 根据平台选择最佳编码器组合
  private getSupportedMimeType(): string {
    const candidates = [
      // macOS Safari/Chrome
      'video/webm;codecs=vp9,opus',
      'video/webm;codecs=vp8,opus',
      // Windows Chrome
      'video/webm;codecs=vp9',
      'video/webm',
      // 回退
      'video/mp4'
    ];

    for (const mime of candidates) {
      if (MediaRecorder.isTypeSupported(mime)) {
        return mime;
      }
    }
    return 'video/webm';
  }
}

关键洞察:OpenScreen 使用纯 Web API(MediaRecorder)而非原生 FFmpeg 来录制视频。这个选择有两面性:

优点

  • 跨平台一致性高(只要浏览器支持,就能跑)
  • 无需 native binding,开发调试成本低
  • Electron 打包体积可控

缺点

  • 导出格式受限于浏览器支持的编码器(主要是 VP8/VP9/AV1)
  • 音频混音能力有限(Web Audio API 功能完整度不如原生)
  • 高码率下 WebM 文件体积可能大于 H.264

不过,OpenScreen 的做法是通过后处理管道来解决编码器限制问题——录制成 WebM 后,导出时可以选择是否调用 FFmpeg 进行转码。

2.4 PixiJS 实时渲染管线

如果说 MediaRecorder 负责"记录",那么 PixiJS 就负责"美化"。这是 OpenScreen 与传统录屏工具最大的技术差异点。

2.4.1 为什么选择 PixiJS?

在深入代码之前,我一直在思考:为什么 OpenScreen 选择 PixiJS 而不是更流行的 Three.js 或者纯 Canvas 2D?

经过分析,我找到了答案:

  1. 2D 场景的效率优势:录屏演示的本质是 2D 画面 + 叠加元素(摄像头框、标注、字幕),3D 渲染能力完全用不上,反而增加 GPU 负担
  2. WebGL 加速:PixiJS 在底层使用 WebGL(GLSL 着色器),可以高效处理缩放、模糊、滤镜等操作,CPU 开销极低
  3. 成熟的多平台支持:PixiJS 8.x 对 macOS Safari(WebKit WebGL)、Windows Chrome(Blink WebGL)、Linux Electron 都有良好兼容
  4. 生态组件丰富:PixiJS 有完整的滤镜(BlurFilter、GlowFilter)、动画(Tween)、交互事件体系

2.4.2 渲染架构

OpenScreen 的 PixiJS 渲染管线设计如下:

// src/canvas/PixiRenderer.ts

export class PixiRenderer {
  private app: Application;
  private mainContainer: Container;
  private webcamContainer: Container;
  private annotationLayer: Container;

  constructor(canvas: HTMLCanvasElement) {
    this.app = new Application();
    await this.app.init({
      canvas,
      width: 1920,
      height: 1080,
      backgroundColor: 0x000000,
      resolution: window.devicePixelRatio || 1,
      autoDensity: true,
      antialias: true,
      preference: 'webgpu', // 优先 WebGPU,回退 WebGL
      stencil: true,
    });
  }

  // 初始化所有渲染层
  async init() {
    // 主容器(放置录屏画面)
    this.mainContainer = new Container();
    this.mainContainer.label = 'main';
    this.app.stage.addChild(this.mainContainer);

    // 摄像头叠加层
    this.webcamContainer = new Container();
    this.webcamContainer.label = 'webcam';
    this.app.stage.addChild(this.webcamContainer);

    // 标注层(箭头、框选、高亮等)
    this.annotationLayer = new Container();
    this.annotationLayer.label = 'annotations';
    this.app.stage.addChild(this.annotationLayer);

    // 字幕层
    this.subtitleLayer = new Container();
    this.subtitleLayer.label = 'subtitles';
    this.app.stage.addChild(this.subtitleLayer);
  }
}

PixiJS 的**渲染层级(Container)**设计天然适配 OpenScreen 的需求:

  • mainContainer:显示录屏画面,接收实时视频帧作为纹理
  • webcamContainer:叠加摄像头画面,支持圆角裁剪和阴影
  • annotationLayer:交互式标注,可在录制时实时绘制
  • subtitleLayer:字幕展示,支持多种样式模板

每一层的渲染顺序由 Container 的 addChild 顺序决定,这个顺序在初始化时就固定了,保证渲染结果的一致性。

2.4.3 摄像头叠加的渲染细节

让我重点看一下摄像头叠加的实现,这是 OpenScreen 最有代表性的视觉特效:

// src/components/WebcamOverlay.ts

export class WebcamOverlay {
  private container: Container;
  private videoSprite: Sprite;
  private mask: Graphics;
  private shadowFilter: Filter;
  private blurBackground: BlurFilter;

  constructor(parent: Container, videoElement: HTMLVideoElement) {
    this.container = new Container();
    parent.addChild(this.container);

    // 1. 将视频元素转为 PixiJS 纹理
    const videoTexture = PIXI.Texture.from(videoElement);
    this.videoSprite = new Sprite(videoTexture);

    // 2. 圆角矩形遮罩
    this.mask = new Graphics();
    const size = { width: 320, height: 240 };
    const radius = 16;
    this.mask.roundRect(0, 0, size.width, size.height, radius);
    this.mask.fill({ color: 0xffffff });
    this.videoSprite.mask = this.mask;
    this.container.addChild(this.mask);
    this.container.addChild(this.videoSprite);

    // 3. 背景模糊(模拟 macOS 的摄像头背景虚化)
    // 使用 BlurFilter + 填充半透明背景实现
    this.blurBackground = new BlurFilter();
    this.blurBackground.blur = 12;
    
    // 4. 外发光边框
    const glowFilter = new GlowFilter({
      distance: 16,
      outerStrength: 2,
      innerStrength: 0,
      color: 0xffffff,
      quality: 0.5
    });
    this.videoSprite.filters = [this.blurBackground, glowFilter];

    // 5. 设置位置(默认右下角悬浮)
    this.container.position.set(
      1920 - size.width - 24,  // 右边缘留 24px
      1080 - size.height - 24   // 下边缘留 24px
    );

    // 6. 添加拖拽交互(用户可自由拖动摄像头位置)
    this.container.eventMode = 'static';
    this.container.cursor = 'pointer';
    this.container.on('pointerdown', this.onDragStart, this);
  }

  // 动态更新视频纹理(每帧调用)
  updateTexture() {
    this.videoSprite.texture.source.update();
  }
}

这个实现用到了 PixiJS 8.x 的几个核心特性:

  • PIXI.Texture.from(videoElement):将 HTML5 Video 元素直接注册为 PixiJS 纹理源,PixiJS 会自动追踪视频帧变化,无需手动更新
  • Graphics.roundRect():PixiJS 8.x 原生支持圆角矩形,相较于 7.x 版本的 hack 方案优雅很多
  • GlowFilter:开源版本提供的发光滤镜,比手写 GLSL 简单得多
  • BlurFilter:背景虚化效果,配合发光滤镜可以模拟出"摄像头悬浮框"的高级感

2.4.4 WebGPU 回退策略

PixiJS 8.x 最大的升级是引入了 WebGPU 后端支持。但目前 WebGPU 在各平台的普及度还不一致:

  • macOS:Safari 17+ 和 Chrome 121+ 均已支持
  • Windows:Chrome 113+ 支持,Firefox Nightly 支持
  • Linux:Chrome 113+ 支持,其他浏览器支持度参差不齐

OpenScreen 的策略是:优先使用 WebGPU,如果初始化失败则回退到 WebGL

// 在 Application.init() 中
preference: 'webgpu',  // 优先 WebGPU

// 如果 WebGPU 不可用,PixiJS 自动回退 WebGL
// 通过 canvas.getContext('webgpu') 检测,不可用时返回 null

这个策略确保了最大范围的兼容性,同时让有 WebGPU 支持的用户能获得更好的渲染性能。

2.5 导出管线:从录制品到成片的最后一跃

录制完成后,用户进入"剪辑"阶段。OpenScreen 的剪辑功能比较克制——只做必要的小修小补,不追求成为专业剪辑软件。主要支持:

  1. 裁剪起止点:拖动时间轴设置录制范围
  2. 缩放动画:预设几种缩放动画(放大鼠标区域、缩放到特定窗口等)
  3. 导出 MP4:调用 electron-builder 打包的 FFmpeg 进行 H.264 转码
  4. 导出 GIF:通过 FFmpeg + palettegen 生成高质量 GIF

导出流程的核心代码结构:

// src/services/export/ExportPipeline.ts

export class ExportPipeline {
  async exportToMP4(
    recordingBlob: Blob,
    options: MP4ExportOptions
  ): Promise<Blob> {
    const { 
      width = 1920, 
      height = 1080, 
      bitrate = '5M',
      fps = 30 
    } = options;

    // 1. 将 WebM Blob 写入临时文件
    const inputPath = await this.writeTempFile(recordingBlob, 'webm');

    // 2. 调用 FFmpeg 进行转码
    // 注意:FFmpeg 是通过 electron-builder 打包进应用资源的
    // 在开发环境需要单独安装
    const outputPath = inputPath.replace('.webm', '.mp4');

    const ffmpegArgs = [
      '-i', inputPath,           // 输入 WebM
      '-c:v', 'libx264',         // H.264 编码
      '-preset', 'medium',       // 编码速度/质量平衡
      '-crf', '23',              // 质量参数(23 = 默认质量)
      '-b:v', bitrate,           // 视频码率
      '-c:a', 'aac',             // AAC 音频编码
      '-b:a', '128k',            // 音频码率
      '-movflags', '+faststart', // 优化 web 播放
      '-pix_fmt', 'yuv420p',     // 兼容性色彩格式
      '-r', String(fps),         // 帧率
      '-s', `${width}x${height}`,// 分辨率
      '-y',                      // 覆盖输出文件
      outputPath
    ];

    await this.runFFmpeg(ffmpegArgs);

    // 3. 读取转码后的文件
    const outputBuffer = await readFile(outputPath);

    // 4. 清理临时文件
    await cleanup([inputPath, outputPath]);

    return new Blob([outputBuffer], { type: 'video/mp4' });
  }

  // GIF 导出(使用 palettegen 优化颜色质量)
  async exportToGIF(
    recordingBlob: Blob,
    options: GIFExportOptions
  ): Promise<Blob> {
    const { width = 480, fps = 15 } = options;
    const inputPath = await this.writeTempFile(recordingBlob, 'webm');
    const palettePath = inputPath.replace('.webm', '_palette.png');
    const outputPath = inputPath.replace('.webm', '.gif');

    // GIF 导出的两步法(高质量彩色 GIF 的标准做法)
    // Step 1: 生成调色板
    await this.runFFmpeg([
      '-i', inputPath,
      '-vf', `fps=${fps},scale=${width}:-1:flags=lanczos,palettegen=stats_mode=diff`,
      '-y', palettePath
    ]);

    // Step 2: 使用调色板编码 GIF
    await this.runFFmpeg([
      '-i', inputPath,
      '-i', palettePath,
      '-lavfi', `fps=${fps},scale=${width}:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`,
      '-y', outputPath
    ]);

    const outputBuffer = await readFile(outputPath);
    await cleanup([inputPath, palettePath, outputPath]);

    return new Blob([outputBuffer], { type: 'image/gif' });
  }
}

GIF 导出的两步法值得展开解释一下:

GIF 格式只支持 256 色,直接转换会导致颜色失真和色块问题。正确的做法是:

  1. 先分析:扫描整个视频,统计每个像素颜色的出现频率,生成一个"调色板"(palette),其中包含 256 个最有代表性的颜色
  2. 再编码:使用这个调色板对每一帧进行颜色映射,保留最大视觉保真度

palettegenpaletteuse 是 FFmpeg 内置的 GIF 优化滤镜组合,这是当前开源社区公认的"最佳 GIF 导出方案",比 GIFsicle、gifsicle 等工具效果更好。


三、生产级优化:从"能跑"到"好用"的工程细节

3.1 内存管理:防止录屏过程中的内存泄漏

录屏应用最大的工程挑战之一是内存管理。录屏一分钟约产生 200MB 的原始数据(5Mbps 码率),如果不做优化,10 分钟的录制就可能占用 2GB 内存。

OpenScreen 通过以下手段控制内存:

3.1.1 流式写入

// 不用 Blob 一次性收集,改用流式写入
class ChunkedRecorder {
  private stream: WritableStream;
  private writer: WritableStreamDefaultWriter;

  async startRecording() {
    const fileHandle = await fs.open('recording.webm', 'w');
    this.stream = fileHandle.writable;
    this.writer = this.stream.getWriter();

    this.mediaRecorder.ondataavailable = async (event) => {
      // 每 100ms 的数据块直接写入磁盘
      // 不在内存中累积
      await this.writer.write(event.data);
    };
  }
}

MediaRecorder.ondataavailable 每 100ms 触发一次,如果直接 push 到数组,10 分钟会累积 6000 个 Blob 片段,内存峰值很高。OpenScreen 在生产环境会改用流式 API,直接写入文件系统。

3.1.2 PixiJS 纹理释放

PixiJS 的纹理是最容易内存泄漏的地方——视频帧不断更新,每帧都生成新的 GPU 纹理,如果不主动释放,显存会持续增长:

// 每隔 N 帧,主动销毁旧纹理
const FRAME_SKIP = 2; // 隔帧采样,降低 GPU 负载

let frameCount = 0;
function onVideoFrame() {
  frameCount++;
  if (frameCount % FRAME_SKIP !== 0) {
    requestAnimationFrame(onVideoFrame);
    return;
  }

  // 只在需要时更新纹理
  if (videoTexture.source.resource?.readyState >= 2) {
    videoTexture.source.update(); // 触发 PixiJS 重新上传到 GPU
  }

  // 每 30 秒检查一次纹理尺寸是否异常
  if (frameCount % (30 * 60) === 0) {
    this.checkTextureHealth();
  }

  requestAnimationFrame(onVideoFrame);
}

3.2 性能监控

OpenScreen 内置了简单的性能监控面板,帮助用户了解录制状态:

// src/services/performance/PerformanceMonitor.ts

export class PerformanceMonitor {
  private stats: {
    fps: number;
    memoryUsed: number;  // MB
    gpuUsage: number;    // 估算百分比
    recordingDuration: number;
    estimatedFileSize: number;
  } = { fps: 0, memoryUsed: 0, gpuUsage: 0, recordingDuration: 0, estimatedFileSize: 0 };

  private rafId: number;
  private lastFrameTime = 0;
  private frameCount = 0;

  start() {
    this.lastFrameTime = performance.now();
    this.tick();
  }

  private tick = () => {
    const now = performance.now();
    const delta = now - this.lastFrameTime;
    this.lastFrameTime = now;
    this.frameCount++;

    // 每秒更新一次统计数据
    if (delta >= 1000) {
      this.stats.fps = Math.round((this.frameCount * 1000) / delta);
      this.stats.memoryUsed = Math.round(
        (performance.memory?.usedJSHeapSize || 0) / 1024 / 1024
      );
      this.stats.recordingDuration = this.getRecordingDuration();
      this.stats.estimatedFileSize = this.estimateFileSize();
      
      this.frameCount = 0;
      this.onStatsUpdate(this.stats);
    }

    this.rafId = requestAnimationFrame(this.tick);
  };
}

这个监控面板在 UI 上显示为录制按钮旁边的一组数字:FPS、内存占用、录制时长、预估文件大小。简单但实用——开发者一眼就能判断录制是否健康。

3.3 跨平台兼容处理

这是 Electron 应用最容易踩坑的地方。OpenScreen 在以下几个关键点做了平台适配:

3.3.1 音频采集

// src/services/audio/AudioCapture.ts

class AudioCaptureService {
  async getAudioStream(options: AudioOptions): Promise<MediaStream> {
    const platform = process.platform; // 'darwin' | 'win32' | 'linux'

    if (platform === 'darwin') {
      // macOS: 使用 systemAudio 采集系统声音
      // 需要 macOS 10.15+ 的屏幕录制权限
      return navigator.mediaDevices.getUserMedia({
        audio: {
          // macOS 上的 electron chromeMediaSource 需要特殊处理
          // 通过 Electron IPC 传递音频源 ID
          mandatory: {
            chromeMediaSource: 'system',
            // macOS 上采集的是整个系统的混合音频
          }
        }
      });
    } else if (platform === 'win32') {
      // Windows: 分为系统音频和应用程序音频
      // Windows 10 1803+ 支持 applicationAudio
      // 更早版本只有 desktop
      const winVersion = os.release().split('.');
      const buildNumber = parseInt(winVersion[2]);
      
      if (buildNumber >= 17134) {
        // Windows 10 1803+ 支持应用级音频采集
        return navigator.mediaDevices.getUserMedia({
          audio: {
            mandatory: {
              chromeMediaSource: 'application',
              // 但需要指定 applicationName
              // 实际使用中会列出所有正在播放音频的应用供用户选择
            }
          }
        });
      } else {
        // 老版本 Windows: 只能录系统混音(不含麦克风)
        // 给用户一个警告
        console.warn('Windows version too old, system audio may not be available');
        return navigator.mediaDevices.getUserMedia({ audio: true });
      }
    } else {
      // Linux: PulseAudio / PipeWire 支持良好
      return navigator.mediaDevices.getUserMedia({
        audio: true
      });
    }
  }
}

3.3.2 窗口边框和标题栏

macOS 和 Windows 的窗口渲染方式有本质区别:

  • macOS 使用无边框窗口frame: false),自定义标题栏,通过 -webkit-app-region: drag 实现拖拽
  • Windows 可以用标准窗口,但自定义标题栏时需要处理最大化/最小化按钮逻辑

OpenScreen 统一采用无边框窗口方案,在渲染进程中根据 process.platform 渲染不同风格的标题栏控件。


四、与同类工具的深度对比

4.1 功能矩阵

特性OpenScreenScreen StudioOBS StudioLoom
价格免费(MIT)$129/年免费免费版有限制
跨平台Win/Mac/Linux仅 macOSWin/Mac/LinuxWin/Mac
导出格式MP4, WebM, GIFMOV, MP4MKV, MP4, FLV...MP4
摄像头叠加⚠️(需插件)
缩放动画
AI 字幕⚠️(规划中)
GIF 导出⚠️(需转码)
CI/CD 集成⚠️
开源可魔改

4.2 技术架构对比

从架构层面看:

OBS Studio 的架构是"专业录制 + 插件生态"——C++ 编写,性能极致,但缺乏"演示创作"层面的抽象。OBS 里的"场景"概念本质上就是一个多源合成器,但 UI 交互复杂,普通用户难以上手。

Screen Studio 的架构是"Electron + 原生录制"——通过 macOS 的 AVFoundation 做录屏,Electron 做 UI,体验最流畅,但受限于 macOS 单一平台。

OpenScreen 的架构是"纯 Web API + PixiJS 增强"——用标准浏览器 API 做录屏,用 PixiJS 做视觉增强。这是一个"够用就好"的折中方案——牺牲了部分性能和平台原生能力,换取了最大的跨平台可移植性和可定制性

4.3 OpenScreen 的核心优势与局限

核心优势

  1. 零成本:MIT 协议,无任何使用限制
  2. 高度可定制:所有功能都暴露在源码中,可以魔改、集成到 CI/CD、做自动化测试录制
  3. 开发者友好:TypeScript + React + Vite 的现代前端技术栈,任何前端工程师都能参与贡献
  4. 社区活跃:Star 增速(每日 +2000+)远超同类开源项目,社区反馈迭代迅速

核心局限

  1. 编码能力依赖浏览器:MediaRecorder 的编码质量受限于浏览器实现,高码率场景下文件体积偏大
  2. 无实时特效:缩放动画等效果是在导出时渲染的,不支持实时预览(但这个需求本身很小众)
  3. 音频处理能力有限:Web Audio API 的混音能力不如原生 API,多轨音频处理场景受限

五、实战:从安装到导出第一个 Demo 视频

5.1 安装

OpenScreen 提供多种安装方式:

# 方式一:直接下载 Release(推荐)
# 访问 https://github.com/siddharthvaddem/openscreen/releases
# 下载对应平台的安装包

# 方式二:从源码构建
git clone https://github.com/siddharthvaddem/openscreen.git
cd openscreen
npm install
npm run build   # Vite 构建 React 应用
npm run package # electron-builder 打包

# 方式三:使用 asdf 进行构建(开发者推荐)
# 项目内置 .tool-versions,自动锁定 Node.js 和 Python 版本
asdf install
npm install

5.2 核心功能使用

录制流程

1. 启动 OpenScreen
2. 选择录制源(屏幕 / 窗口 / 区域)
3. 配置录制选项:
   - 是否采集系统音频
   - 是否采集麦克风
   - 摄像头叠加位置
   - 输出分辨率
4. 点击录制
5. 操作你的演示内容
6. 点击停止
7. 在时间轴上微调起止点
8. 选择导出格式(MP4/GIF)
9. 等待导出完成

API 集成(自动化场景)

对于需要自动化录制 CI/CD 流程的高级用户:

// src/services/automation/AutoRecord.ts

export async function automatedRecording(config: AutoRecordConfig) {
  const { 
    sourceId, 
    duration = 60_000,  // 默认录制 60 秒
    outputDir = './recordings',
    format = 'mp4'
  } = config;

  // 1. 初始化录制引擎
  const engine = new RecordingEngine();
  await engine.startRecording(sourceId, {
    captureAudio: true,
    bitrate: 5_000_000
  });

  // 2. 定时停止
  setTimeout(async () => {
    await engine.stopRecording();
    
    // 3. 获取录制数据
    const blob = await engine.getBlob();
    
    // 4. 导出
    const pipeline = new ExportPipeline();
    const finalBlob = format === 'mp4' 
      ? await pipeline.exportToMP4(blob, { bitrate: '5M' })
      : await pipeline.exportToGIF(blob, { fps: 15 });
    
    // 5. 保存
    await saveToFile(finalBlob, `${outputDir}/demo.${format}`);
    
    console.log('Recording saved to', `${outputDir}/demo.${format}`);
  }, duration);
}

这个 API 设计允许 OpenScreen 被集成到 CI/CD 管道中——例如每次代码合并后自动录制一个功能 Demo,并上传到内部知识库。


六、生态与社区:开源项目可持续发展的样本

6.1 Fork 生态

OpenScreen 的另一个值得关注的现象是其Fork 生态。截至 2026 年 4 月,GitHub 上已经有超过 50 个活跃的 Fork,其中几个值得关注:

Recordly(webadderall/Recordly):

  • 从 OpenScreen Fork 而来,定位是"增强版"
  • 新增特性:自动缩放、动态模糊光标、摄像头叠加自动跟踪、实时字幕生成
  • 适合需要更高制作质量的进阶用户

OpenScreen-CI

  • 一个 GitHub Action 封装,将 OpenScreen 的录制能力集成到 CI/CD
  • 用法示例:每次 PR 合并后,自动录制该功能模块的 Demo 视频

OpenScreen-Plugin-SDK(社区开发中):

  • 插件 SDK,允许第三方开发者为 OpenScreen 开发插件
  • 计划支持:自定义转场动画、AI 自动剪辑、视频水印等

6.2 社区贡献图谱

分析 OpenScreen 的贡献者结构可以发现:

  • 核心贡献者:2 人(siddharthvaddem + 1 位协作者),负责核心架构和重大功能
  • 功能贡献者:~15 人,通过 PR 贡献了各种 UI 改进、Bug 修复、平台适配
  • 文档贡献者:~30 人,包括多语言翻译、教程撰写、截图更新

这种"1-2 人核心 + 社区共建"的模式,在中小型开源工具中非常典型,也是最可持续的模式之一——避免了大项目常见的"维护者倦怠"问题。

6.3 商业模式探索

目前 OpenScreen 完全免费,没有明确的商业模式。但从创始人 siddharthvaddem 的公开言论来看,他提到了几种可能的方向:

  1. OpenScreen Cloud:付费云端渲染服务(导出时调用云端 GPU 进行加速)
  2. 企业版:增强的团队协作功能(多用户、版本管理、权限控制)
  3. OpenScreen Pro:一次性买断高级功能(AI 字幕、更多导出格式)

不过这些都还在探索阶段。对于一个刚上线不到一个月、Star 数就已经突破 2 万的项目来说,先把产品做好显然比急着商业化更重要。


七、未来展望:OpenScreen 的演进方向

7.1 近期路线图

根据 GitHub Issues 和 Discussions 的梳理,OpenScreen 接下来的开发重点包括:

Q2 2026

  • AI 字幕自动生成(集成 Whisper API)
  • 绿幕/背景替换功能
  • 多轨时间轴(支持同时录制多个窗口并剪辑)
  • Windows 原生音频采集优化

Q3 2026

  • WebDAV/云盘直传(导出后直接上传到 Google Drive/Dropbox)
  • 插件系统 SDK
  • API 文档(吸引自动化集成用户)

7.2 技术演进方向

从技术趋势看,OpenScreen 未来有几个值得关注的演进方向:

WebGPU 深度集成:当 WebGPU 在各平台普及后,PixiJS 可以充分利用现代 GPU 的计算着色器能力,实现实时的 AI 特效(如背景分割、虚拟背景)。

Whisper 实时字幕:通过 WebAssembly 版本的 Whisper,在录制过程中实时生成字幕,无需上传到云端。

Spatial Audio 支持:录制过程中保留空间音频信息(macOS Spatial Audio、Windows Dolby Atmos),导出后保持沉浸式音频体验。

7.3 竞争格局演变

随着 OpenScreen 的走红,可以预见:

  1. Screen Studio 可能会降价或推出免费版来应对竞争
  2. Loom 可能会加速推出桌面端原生功能(目前 Loom 主要靠浏览器扩展)
  3. 更多开源社区会 Fork OpenScreen,形成工具矩阵

但最终受益的是开发者本身——我们有了一个真正自由、可定制、功能完整的录屏工具,不再被水印和订阅制绑架。


结语

写完这篇文章,我最大的感受是:好的工具不一定是技术最先进的,但一定是解决痛点最精准的

OpenScreen 没有用任何"炫技"的技术。它的 Electron + React + PixiJS 栈,任何一个前端工程师都能理解。它的 Web API 录制方案,远没有 FFmpeg 原生集成性能好。但它解决了开发者内容创作中"最后一公里"的困境——零成本、无限制、够好用。

更重要的是,它开源、可定制、能集成 CI/CD。对于一个天天写代码的人来说,这意味着你可以用写代码的方式来解决录制问题——这是所有商业工具都无法提供的可能性。

开源的力量从来不在于"免费",而在于把可能性还给你


参考资料

  • OpenScreen GitHub 仓库:https://github.com/siddharthvaddem/openscreen
  • PixiJS 官方文档:https://pixijs.com/
  • Electron desktopCapturer API:https://www.electronjs.org/docs/api/desktop-capturer
  • MediaRecorder API 兼容性:https://caniuse.com/mediarecorder
  • FFmpeg GIF 导出最佳实践:https://engineering.giphy.com/blog/making-gifs-with-ffmpeg/

本文作者:程序员茄子,2026 年 4 月 17 日。

复制全文 生成海报 Electron PixiJS 开源 录屏 TypeScript React

推荐文章

nuxt.js服务端渲染框架
2024-11-17 18:20:42 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
pycm:一个强大的混淆矩阵库
2024-11-18 16:17:54 +0800 CST
ElasticSearch集群搭建指南
2024-11-19 02:31:21 +0800 CST
windon安装beego框架记录
2024-11-19 09:55:33 +0800 CST
Vue 3 中的 Watch 实现及最佳实践
2024-11-18 22:18:40 +0800 CST
Vue3中的v-for指令有什么新特性?
2024-11-18 12:34:09 +0800 CST
2024年公司官方网站建设费用解析
2024-11-18 20:21:19 +0800 CST
15 个 JavaScript 性能优化技巧
2024-11-19 07:52:10 +0800 CST
API 管理系统售卖系统
2024-11-19 08:54:18 +0800 CST
Vue3中如何处理权限控制?
2024-11-18 05:36:30 +0800 CST
使用xshell上传和下载文件
2024-11-18 12:55:11 +0800 CST
Nginx 如何防止 DDoS 攻击
2024-11-18 21:51:48 +0800 CST
实用MySQL函数
2024-11-19 03:00:12 +0800 CST
php使用文件锁解决少量并发问题
2024-11-17 05:07:57 +0800 CST
js生成器函数
2024-11-18 15:21:08 +0800 CST
Golang 中你应该知道的 noCopy 策略
2024-11-19 05:40:53 +0800 CST
程序员茄子在线接单