采集:摄像头/麦克风与屏幕共享

采集是 RTC 流水线的起点,负责从硬件设备获取原始音视频数据。本章将从原理层面介绍采集机制、数据格式、内存模型和平台差异。

1. 采集概述

1.1 采集的本质

采集是将物理世界的模拟信号转换为数字信号的过程:

物理信号 → 传感器 → ADC(模数转换)→ 数字数据
   ↓
摄像头:光线 → CMOS/CCD → ADC → YUV/RGB
   ↓
麦克风:声波 → 振膜 → ADC → PCM

1.2 采集流水线

flowchart LR
    subgraph 物理层
        A[光线/声波]
    end

    subgraph 硬件层
        B[传感器]
        C[ADC]
    end

    subgraph 驱动层
        D[设备驱动]
    end

    subgraph 系统层
        E[系统采集API]
    end

    subgraph 应用层
        F[MediaStream]
    end

    A --> B --> C --> D --> E --> F

2. 视频采集原理

2.1 图像传感器工作原理

传感器类型 工作原理 特点
CMOS 每个像素独立放大读出 功耗低、集成度高、主流选择
CCD 电荷逐行转移读出 画质好、功耗高、专业设备

2.2 拜耳阵列与色彩还原

传感器只能感知光强,需要拜耳滤镜获取色彩:

拜耳阵列排列:
R G R G R G
G B G B G B
R G R G R G
G B G B G B

R=红 G=绿 B=蓝
每个位置只能捕获一种颜色,需要去马赛克算法还原完整RGB

2.3 视频采集输出格式

原始格式(从传感器输出)

格式 位深 数据量(1080p) 说明
RAW 10-14 bit 极大 未经处理的传感器数据
RGB24 24 bit 6 MB/帧 红绿蓝各8位
RGBA 32 bit 8 MB/帧 含透明通道

YUV 格式(视频处理标准)

格式 色度采样 数据量 适用场景
I420 (YUV420P) 4:2:0 1.5 MB/帧 WebRTC 标准格式
NV12 (YUV420SP) 4:2:0 1.5 MB/帧 GPU 高效处理
I422 (YUV422P) 4:2:2 2 MB/帧 色彩质量更高
I444 (YUV444P) 4:4:4 3 MB/帧 专业视频

YUV 格式内存布局

I420 (Planar) - Y/U/V 分开存储:
┌─────────────────────┐
│ Y 平面 (1920x1080)  │ 亮度,完整分辨率
├──────────┬──────────┤
│ U 平面   │ V 平面   │ 色度,1/4 分辨率
│(960x540) │(960x540) │
└──────────┴──────────┘

NV12 (Semi-Planar) - Y 独立,UV 交错:
┌─────────────────────┐
│ Y 平面 (1920x1080)  │
├─────────────────────┤
│ UV 交错 (960x540)   │ U/V 交替存储
│ U V U V U V ...     │
└─────────────────────┘

2.4 采集分辨率与帧率

常见分辨率

名称 分辨率 像素数 宽高比
VGA 640×480 307K 4:3
HD 1280×720 922K 16:9
FHD 1920×1080 2.07M 16:9
2K 2560×1440 3.69M 16:9
4K 3840×2160 8.29M 16:9

帧率选择

帧率 适用场景 说明
15 fps 静态内容、弱网 节省带宽
24 fps 电影标准 有运动模糊
30 fps 标准视频通话 平衡流畅度与带宽
60 fps 游戏、运动 高流畅度
120+ fps 慢动作、VR 特殊需求

3. 音频采集原理

3.1 麦克风工作原理

flowchart LR
    A[声波] --> B[振膜振动]
    B --> C[电容/动圈转换]
    C --> D[电信号]
    D --> E[放大器]
    E --> F[ADC]
    F --> G[PCM数据]

3.2 音频采集参数

参数 说明 常见值
采样率 每秒采样次数 8000/16000/44100/48000 Hz
位深 每个采样的位数 16/24/32 bit
声道数 音频轨道数 1(单声道)/ 2(立体声)
帧大小 每次采集的采样数 480/960/1024

3.3 音频数据量计算

数据量 = 采样率 × 位深 × 声道数

示例(48kHz/16bit/单声道):
48000 × 16 × 1 = 768 kbps = 96 KB/s

每 20ms 帧大小:
48000 × 0.02 × 2字节 = 1920 字节

3.4 音频采集格式

格式 说明 应用场景
PCM(整数) 原始采样值 最通用
浮点 PCM -1.0 ~ 1.0 范围 专业音频处理
μ-law/A-law 对数压缩 传统电话

4. 内存模型与数据流

4.1 采集缓冲区模型

flowchart LR
    subgraph 硬件缓冲
        A[传感器] --> B[环形缓冲]
    end

    subgraph 驱动缓冲
        B --> C[驱动队列]
    end

    subgraph 应用缓冲
        C --> D[应用缓冲区]
    end

    subgraph 处理
        D --> E[编码/处理]
    end

4.2 缓冲区大小权衡

缓冲区大小 延迟 稳定性 适用场景
小(10ms) 易丢数据 实时通话
中(20-40ms) 中等 平衡 通用场景
大(100ms+) 稳定 录制

4.3 采集到内存 vs 采集到纹理

这是视频采集中最关键的性能优化点,决定了数据如何在系统中流转。

两种采集模式对比

采集到内存(CPU 可访问):

摄像头 → DMA → 系统内存 → CPU 处理 → 复制到显存 → GPU 渲染
                    ↑
              CPU 可以直接访问
              但需要额外复制到 GPU


采集到纹理(GPU 直接访问):

摄像头 → DMA → 显存纹理 → GPU 渲染/编码
                    ↑
              GPU 直接访问
              无需 CPU 参与
特性 采集到内存 采集到纹理
数据位置 系统内存(RAM) 显存(VRAM)
CPU 访问 直接访问 需要映射或回读
GPU 访问 需要上传到显存 直接访问
内存拷贝 需要 CPU→GPU 拷贝 无拷贝
适用场景 CPU 处理、编码 渲染、GPU 编码
延迟 较高(多一次拷贝) 较低

各平台纹理采集支持

平台 API 纹理类型
Windows Media Foundation + D3D11 ID3D11Texture2D
macOS AVFoundation + Metal MTLTexture
iOS AVCaptureSession + Metal MTLTexture
Android Camera2 + SurfaceTexture SurfaceTexture / OpenGL ES
浏览器 WebRTC 内部处理 WebGL Texture

4.4 内存拷贝问题与零拷贝优化

传统采集流程的拷贝开销

传统流程(多次拷贝):

┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐    ┌─────────┐
│ 摄像头  │───▶│驱动缓冲 │───▶│系统缓冲 │───▶│应用缓冲 │───▶│显存缓冲 │
│ DMA    │    │(内核态) │    │(用户态) │    │(处理)   │    │(渲染)   │
└─────────┘    └─────────┘    └─────────┘    └─────────┘    └─────────┘
                    │              │              │              │
                    └──────────────┴──────────────┴──────────────┘
                              拷贝 1    拷贝 2    拷贝 3

1080p@30fps 每秒数据量:1920×1080×1.5×30 ≈ 93 MB/s
3 次拷贝 = 279 MB/s 内存带宽占用

零拷贝优化方案

方案 原理 拷贝次数 适用场景
共享内存 应用直接访问驱动缓冲 1 跨进程通信
内存映射(mmap) 将内核缓冲映射到用户态 1 Linux
Direct3D Surface 采集直接到 GPU 纹理 0 Windows
Metal Texture 采集直接到 Metal 纹理 0 Apple
SurfaceTexture 采集到 OpenGL 纹理 0 Android

零拷贝采集流程

零拷贝流程:

┌─────────┐    ┌─────────────────┐    ┌─────────┐
│ 摄像头  │───▶│ 共享缓冲/纹理   │───▶│ 消费者  │
│ DMA    │    │ (无拷贝传递)    │    │         │
└─────────┘    └─────────────────┘    └─────────┘

消费者可以是:
- GPU 渲染管线(直接绑定纹理)
- 硬件编码器(直接编码纹理)
- CPU 处理(按需映射访问)

4.5 避免内存到显存拷贝的策略

为什么内存到显存拷贝代价高

内存到显存拷贝的代价:

系统内存 ──────────────────▶ 显存
   │                           │
   │  PCIe 总线传输            │
   │  带宽约 16 GB/s           │
   │  1080p@30fps ≈ 93 MB/s   │
   │                           │
   └───────────────────────────┘

问题:
1. 占用 PCIe 带宽,影响其他传输
2. CPU 需要参与调度
3. 增加延迟
4. 功耗增加

优化策略一览

┌────────────────────────────────────────────────────────────┐
│                    避免内存→显存拷贝                        │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  1. 采集直出纹理                                           │
│     摄像头 ──▶ GPU 纹理 ──▶ 渲染/编码                     │
│     适用于:渲染预览、硬件编码                             │
│                                                            │
│  2. 共享纹理跨组件                                         │
│     采集纹理 ──▶ 共享句柄 ──▶ 编码器/渲染器               │
│     适用于:多组件协作                                     │
│                                                            │
│  3. GPU 上完成所有处理                                     │
│     纹理 ──▶ GPU 处理 ──▶ GPU 编码                        │
│     适用于:美颜、滤镜等                                   │
│                                                            │
│  4. 仅在必要时回读                                         │
│     纹理 ──▶ (按需) ──▶ CPU 处理                          │
│     适用于:需要 CPU 分析的场景                            │
│                                                            │
└────────────────────────────────────────────────────────────┘

各平台共享纹理机制

平台 共享机制 跨组件共享
Windows ID3D11Texture2D + 共享句柄 D3D11 ↔ D3D12 ↔ CUDA
macOS/iOS MTLTexture + IOSurface Metal ↔ OpenGL ↔ CoreAnimation
Android SurfaceTexture / HardwareBuffer Camera ↔ OpenGL ↔ MediaCodec
Linux DMABUF / EGLImage V4L2 ↔ OpenGL ↔ VA-API

4.6 采集数据流转场景分析

场景一:仅渲染预览(最简单)

采集 ──▶ 纹理 ──▶ 渲染到屏幕

最优路径:摄像头 → GPU 纹理 → 直接渲染
拷贝次数:0
CPU 参与:几乎无

场景二:渲染 + 软编码

采集 ──▶ 纹理 ──▶ 渲染
         │
         └──▶ CPU 回读 ──▶ 软编码

需要权衡:
- 回读会触发 GPU→CPU 拷贝
- 可以降低回读频率或分辨率
- 或改用硬件编码

场景三:渲染 + 硬编码

采集 ──▶ 共享纹理 ──┬──▶ 渲染
                    └──▶ 硬件编码器

最优路径:纹理共享,无拷贝
拷贝次数:0
编码效率:高

场景四:渲染 + 硬编码 + CPU 处理

采集 ──▶ 纹理 ──┬──▶ 渲染
                ├──▶ 硬件编码
                └──▶ 降采样回读 ──▶ CPU 处理

优化:
- CPU 处理使用降采样后的数据
- 减少回读数据量
- 异步回读避免阻塞

4.7 零拷贝实现注意事项

数据生命周期管理

零拷贝的数据生命周期挑战:

传统拷贝模式:
  原数据 ──拷贝──▶ 新数据
  原数据可以立即释放

零拷贝模式:
  原数据 ──引用──▶ 消费者A
         ──引用──▶ 消费者B
  需要等所有消费者使用完毕才能释放

解决方案:
- 引用计数
- 信号量/条件变量
- 回调通知
- 固定缓冲池 + 轮转

线程安全与同步

生产者-消费者同步:

生产者(采集线程)          消费者(处理线程)
       │                          │
       ▼                          ▼
   写入缓冲区                 读取缓冲区
       │                          │
       ├─────────同步点──────────┤
                 │
           需要保证:
           - 写入完成后再可读
           - 读取时不被覆盖
           - 缓冲区轮转正确

内存对齐与格式约束

约束 说明 影响
内存对齐 GPU 纹理通常要求 16/64 字节对齐 影响缓冲区分配策略
格式匹配 采集格式必须与消费格式一致 可能需要格式转换
跨平台差异 不同平台的纹理格式不同 需要抽象层适配

4.8 数据所有权与生命周期

采集数据流通过程中的所有权转移:

硬件 → 驱动 → 系统API → 应用 → 编码器
  │      │       │        │       │
  └──────┴───────┴────────┴───────┘
        需要明确数据何时可以释放

关键问题:
- 谁分配内存?
- 谁负责释放?
- 数据是否需要复制?
- 引用计数如何管理?

5. 各平台采集机制

5.1 Windows 采集架构

flowchart TB
    subgraph 应用层
        A[应用程序]
    end

    subgraph API层
        B[Media Foundation]
        C[DirectShow]
        D[WASAPI]
    end

    subgraph 系统层
        E[AVStream 驱动]
    end

    subgraph 硬件层
        F[摄像头/麦克风]
    end

    A --> B --> E --> F
    A --> C --> E
    A --> D --> E
API 用途 特点
Media Foundation 现代 Windows 视频/音频 推荐,功能完整
DirectShow 传统视频采集 兼容性好
WASAPI 低延迟音频 专业音频
WDM/KS 内核级访问 最低延迟

5.2 macOS/iOS 采集架构

flowchart TB
    subgraph 应用层
        A[应用程序]
    end

    subgraph 框架层
        B[AVFoundation]
        C[CoreAudio]
        D[ScreenCaptureKit]
    end

    subgraph 系统层
        E[IOKit]
        F[CoreMedia]
    end

    subgraph 硬件层
        G[摄像头/麦克风]
    end

    A --> B --> E --> G
    A --> C --> F
    A --> D
框架 用途 特点
AVFoundation 视频/音频采集 现代 API,推荐
CoreAudio 音频处理 底层,灵活
ScreenCaptureKit 屏幕采集 macOS 12.3+
CoreMediaIO 设备访问 底层框架

5.3 Android 采集架构

flowchart TB
    subgraph 应用层
        A[App]
    end

    subgraph Framework层
        B[Camera2 API]
        C[CameraX]
        D[AudioRecord]
        E[MediaProjection]
    end

    subgraph Native层
        F[Camera HAL]
        G[Audio HAL]
    end

    subgraph 内核层
        H[V4L2]
        I[ALSA]
    end

    A --> B --> F --> H
    A --> C --> B
    A --> D --> G --> I
    A --> E
API 用途 特点
Camera2 视频采集 底层控制,复杂
CameraX 视频采集 简化版,推荐
AudioRecord 音频采集 原始 PCM
MediaProjection 屏幕采集 需要用户授权

5.4 Linux 采集架构

flowchart TB
    subgraph 应用层
        A[应用程序]
    end

    subgraph 库层
        B[libv4l]
        C[ALSA/PulseAudio/PipeWire]
    end

    subgraph 内核层
        D[V4L2]
        E[ALSA]
    end

    subgraph 硬件层
        F[设备]
    end

    A --> B --> D --> F
    A --> C --> E
子系统 用途 说明
V4L2 视频采集 Video for Linux 2
ALSA 音频采集 底层音频
PulseAudio 音频服务 用户态音频服务
PipeWire 现代 media 框架 整合音视频

5.5 浏览器采集机制

flowchart TB
    subgraph Web应用
        A[JavaScript]
    end

    subgraph Web API
        B[getUserMedia]
        C[getDisplayMedia]
        D[enumerateDevices]
    end

    subgraph 浏览器引擎
        E[MediaStream 实现]
        F[平台适配层]
    end

    subgraph 系统API
        G[Windows/macOS/Linux API]
    end

    A --> B --> E --> F --> G
    A --> C
    A --> D

6. 屏幕采集原理

6.1 屏幕采集方式

方式 原理 性能 兼容性
GDI/BitBlt 系统截图 API 一般 Windows
DXGI Desktop Duplication GPU 级别复制 高效 Windows 8+
X11/XDamage X Window 截图 一般 Linux
CoreGraphics macOS 截图 一般 macOS
ScreenCaptureKit 现代框架 高效 macOS 12.3+

6.2 屏幕采集优化技术

技术 说明 效果
脏区域检测 只捕获变化区域 减少 CPU
硬件加速 GPU 直接复制 减少 CPU
光标分离 光标单独处理 效率提升
帧率自适应 根据内容调整 节省资源

6.3 屏幕采集 vs 摄像头采集

特性 屏幕采集 摄像头采集
帧率需求 较低(5-15fps) 较高(30fps)
分辨率 高(1080p-4K) 中(720p-1080p)
内容类型 静态为主,文字 动态为主,人脸
编码策略 屏幕内容编码优化 自然视频编码

7. 采集注意事项

7.1 时序与同步

问题 原因 解决方案
音视频不同步 独立采集,时钟漂移 统一时间戳基准
帧间隔不均匀 系统调度、缓冲抖动 使用时间戳而非系统时间
丢帧 处理跟不上采集 合理的缓冲区设计

7.2 资源管理

采集设备是独占资源,需要正确管理生命周期:

1. 打开设备 → 获取访问权
2. 配置参数 → 设置格式、分辨率等
3. 开始采集 → 数据持续输出
4. 停止采集 → 停止数据流
5. 释放设备 → 允许其他应用使用

常见问题:
- 设备未释放导致其他应用无法访问
- 多次打开同一设备
- 异常情况下资源泄漏

7.3 权限模型

平台 权限机制 说明
浏览器 MediaDevices API 用户弹窗授权
iOS Info.plist 声明 首次使用弹窗
Android Manifest 声明 + 运行时权限 Android 6.0+
macOS TCC 权限 系统偏好设置
Windows UAC / 隐私设置 Windows 10+

7.4 热插拔处理

设备热插拔事件流:

设备插入 → 系统检测 → 驱动加载 → 设备可用 → 通知应用
                                              ↓
                                        应用处理:
                                        - 枚举新设备
                                        - 更新 UI
                                        - 可能自动切换

设备拔出 → 系统检测 → 驱动卸载 → 设备不可用 → 通知应用
                                              ↓
                                        应用处理:
                                        - 停止使用该设备
                                        - 切换到其他设备
                                        - 更新 UI

7.5 性能考量

因素 影响 优化建议
分辨率 CPU/GPU 负载 按需选择,不要过高
帧率 处理频率 实时通话 30fps 足够
格式转换 CPU 消耗 优先使用硬件支持的格式
缓冲区大小 延迟 vs 稳定 根据场景权衡

8. 多设备与并发采集

8.1 多摄像头采集

场景:同时使用前置和后置摄像头

挑战:
- 大多数系统 API 一次只能打开一个摄像头
- 需要特殊的多摄 API 支持
- 数据量翻倍,处理压力增大

解决方案:
- Android: CameraManager 支持多摄
- iOS: AVCaptureMultiCamSession
- 浏览器: 分别调用 getUserMedia(受限)

8.2 音视频同步采集

flowchart TB
    subgraph 采集
        A[视频采集] --> C[时间戳 T1]
        B[音频采集] --> D[时间戳 T2]
    end

    subgraph 同步
        C --> E[共享时钟基准]
        D --> E
        E --> F[计算相对偏移]
    end

    subgraph 输出
        F --> G[同步的音视频流]
    end

8.3 屏幕共享 + 摄像头组合

常见布局模式:

画中画模式:
┌────────────────────────────┐
│                            │
│      屏幕共享内容          │
│                            │
│  ┌────────┐               │
│  │ 摄像头  │               │
│  └────────┘               │
└────────────────────────────┘

并排模式:
┌──────────────┬─────────────┐
│              │             │
│  屏幕共享    │   摄像头    │
│              │             │
└──────────────┴─────────────┘

9. 常见问题与排查

9.1 采集失败原因

问题 原因 排查方向
黑屏 设备未就绪、权限问题 检查设备状态和权限
无声音 设备选择错误、静音 检查设备配置
帧率低 系统负载高、参数过高 降低分辨率/帧率
延迟大 缓冲区过大 减小缓冲区
花屏 格式不匹配、内存问题 检查格式协商

9.2 跨平台兼容性

差异点 Windows macOS Linux 移动端
默认格式 NV12 BGRA I420 NV21/YV12
权限 系统设置 TCC XDG 运行时
设备枚举 Media Foundation AVFoundation V4L2 CameraManager
屏幕采集 DXGI ScreenCaptureKit X11/Pipewire MediaProjection

10. 总结

采集是 RTC 的起点,需要理解以下核心概念:

模块 关键点
视频采集 传感器原理、YUV 格式、分辨率帧率选择
音频采集 采样率、位深、PCM 格式
内存模型 缓冲区设计、零拷贝、数据所有权
平台差异 各系统 API、格式偏好、权限机制
屏幕采集 脏区域检测、硬件加速、内容类型
资源管理 设备生命周期、热插拔、并发采集

下一章将介绍如何将采集到的数据进行渲染显示。