capture_multiple_sink

Example of the component espressif/esp_capture v1.0.5
# ESP 多 Sink 采集示例

- [English Version](./README.md)
- 例程难度:⭐⭐⭐

## 例程简介

- 本例程演示如何用 **一套摄像头采集系统**,通过 `esp_capture` 为多个消费者提供不同输出。
- 每个 sink 可独立开启 / 关闭,既可连续运行,也可 one-shot 抓拍。
- 主程序:`main/capture_multiple_sink.c`
- Sink 配置:`main/settings.h`

### 典型场景

- 视频推流 + 本地显示
- 视频推流 + 抓拍
- 实时预览 + AI / 图像处理
- 事件触发或低内存 one-shot 抓拍
- 多路不同分辨率 / 格式的消费者

## 环境配置

### 硬件要求

- 推荐开发板:[ESP32-P4-Function-EV-Board](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/user_guide.html)(多分辨率 / PPA 缩放路径面向 ESP32-P4)
- 摄像头模组(CSI)与音频输入设备(ADC 麦克风)
- **不需要** SD 卡(帧在内存中直接校验)

### 默认 IDF 分支

本例程支持 IDF `release/v5.5` (>= v5.5.2)。

## 编译和下载

### 选择并配置开发板

本示例使用 [ESP Board Manager](https://github.com/espressif/esp-board-manager) 管理板级资源。推荐安装辅助工具 [`esp-bmgr-assist`](https://pypi.org/project/esp-bmgr-assist/) 作为默认入口。

在已激活的 ESP-IDF Python 环境下安装(同一环境只需安装一次):

```bash
pip install esp-bmgr-assist
pip install --upgrade esp-bmgr-assist  # 当提示需要更新时执行此命令
```

- 查看支持的板子:

```bash
idf.py bmgr -l
```

输出示例:

```text
ℹ️  Board Components:
  espressif/esp_boards:
    [1] esp32_c3_lyra
    [2] esp32_lyrat_4_3
    [3] esp32_lyrat_mini_1_1
    [4] esp32_p4_eye
    [5] esp32_p4_function_ev_board
    [6] esp32_s31_function_coreboard_1
    [7] esp32_s31_korvo_1
    [8] esp32_s3_box_3
    [9] esp32_s3_box_lite
    [10] esp32_s3_korvo_2_3
```

不同 `esp_board_manager` 版本或自定义开发板依赖可能会使列表和序号变化,使用时以 `idf.py bmgr -l` 的实际输出为准。

- 选择开发板:

```bash
idf.py bmgr -b <board_index|board_name>
```

例如选择 `esp32_p4_function_ev_board`:

```bash
idf.py bmgr -b 5
# 或
idf.py bmgr -b esp32_p4_function_ev_board
```

首次执行 `idf.py bmgr` 时,组件会根据本工程 `main/idf_component.yml` 中声明的 `espressif/esp_board_manager` 依赖自动下载。

> [!NOTE]
> 如果切换为其他 `esp_board_manager` 支持的开发板,请按相同步骤执行并替换板型名称/索引。
> 自定义开发板请参考 [创建开发板指南](https://docs.espressif.com/projects/esp-board-manager/zh_CN/latest/create-board/index.html)。
> `esp_board_manager` 更多信息请参考 [ESP_BOARD_MANAGER 入门指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/README_CN.md)

## 如何使用例程

### 流程介绍

```mermaid
flowchart LR
    Camera["摄像头"] --> Capture["采集系统"]

    Capture --> Stream["推流<br/>H.264 + AAC"]
    Capture --> Display["本地显示<br/>RGB565"]
    Capture --> Snapshot["抓拍<br/>JPEG"]
```

所有输出共享 **同一套采集系统**,无需为每个消费者再开一套 capture。

### 功能和用法

`app_main()` 只构建一次采集系统,然后依次运行四个场景。每个场景均为 **enable sinks → start → run → stop**(便于产品代码直接拷贝)。最后统一销毁 capture。

1. **仅推流** — 开启 sink 0,关闭 sink 1/2,运行 30 秒
2. **推流 + 显示** — 开启 sink 0 与 2,运行 30 秒;静音推流 10 秒;再恢复推流 10 秒
3. **JPEG one-shot(快速)** — sink 0 持续运行,sink 1 连续两次 one-shot 抓拍
4. **JPEG one-shot(省内存)** — 仅在抓拍时开启 sink 1,抓完立刻关闭

其他相关行为:

- 注册默认视频 / 音频编码器(`esp_video_enc_register_default`、`esp_audio_enc_register_default`)
- 通过 `esp_capture_set_thread_scheduler` 设置线程调度参数
- 不写 SD 卡;在内存中校验帧:
  - **H.264** — Annex-B 起始码(`00 00 01` 或 `00 00 00 01`)
  - **JPEG** — SOI(`FF D8`)与 EOI(`FF D9`)
  - **RGB565** — 帧长度等于 `width × height × 2`

#### 场景示意

**1. 仅推流**

```mermaid
flowchart LR
    Camera["摄像头"] --> Capture["采集"]
    Capture --> Stream["Sink 0<br/>H.264 + AAC<br/>推流"]

    Display["Sink 2<br/>关闭"]
    Snapshot["Sink 1<br/>关闭"]

    Capture -.-> Display
    Capture -.-> Snapshot
```

默认推流配置:1920×1080 @ 25 fps H.264 + AAC 16 kHz / 单声道。

**2. 推流 + 本地显示**

```mermaid
flowchart LR
    Camera["摄像头"] --> Capture["采集"]

    Capture --> Stream["Sink 0<br/>H.264 + AAC<br/>推流"]
    Capture --> Display["Sink 2<br/>RGB565<br/>本地显示"]

    Capture -.-> Snapshot["Sink 1<br/>关闭"]
```

适用于同时需要远端推流与本地预览的摄像头、门铃、监视器等设备。

**3. 推流 + 抓拍**

```mermaid
flowchart LR
    Camera["摄像头"] --> Capture["采集"]

    Capture --> Stream["Sink 0<br/>H.264 + AAC<br/>推流"]
    Capture --> Snapshot["Sink 1<br/>JPEG<br/>抓拍"]

    Capture -.-> Display["Sink 2<br/>关闭"]
```

```mermaid
sequenceDiagram
    participant App as 应用
    participant Cap as 采集
    participant Stream as 推流
    participant JPEG as JPEG Sink

    App->>Cap: Start
    Cap->>Stream: 连续视频

    App->>JPEG: Enable
    Cap->>JPEG: 采集 JPEG
    JPEG-->>App: 抓拍完成
    App->>JPEG: Disable

    Cap->>Stream: 继续推流
```

**4. 低内存 one-shot 抓拍**

```mermaid
flowchart TD
    Start["正常运行"] --> Streaming["推流 Sink 开启"]
    Streaming --> Request["需要抓拍"]

    Request --> Enable["开启 JPEG Sink"]
    Enable --> Capture["采集一张 JPEG"]
    Capture --> Disable["关闭 JPEG Sink"]

    Disable --> Streaming
```

### 配置说明

Sink 通过 `main/settings.h` 中的 `CAPTURE_SINKS_SETTINGS` 配置。

默认 Sink:

| 下标 | 用途 | 默认 |
| ---- | ---- | ---- |
| 0 | 推流 | 1920×1080 @ 25 H.264 + AAC 16 kHz / 1ch |
| 1 | 抓拍 | 2560×1440 @ 1 JPEG |
| 2 | 显示 | 320×240 @ 20 RGB565 |

`SINK_NUM` 由数组长度自动推导。若数量超过 `CONFIG_ESP_CAPTURE_MAX_SINK_NUM`,编译会失败。

适配产品应用时可:

1. 修改 `main/settings.h` 中的 `CAPTURE_SINKS_SETTINGS`
2. 为每个 sink 设置分辨率、帧率与格式
3. 持续需要的输出保持开启;偶发抓拍使用 one-shot
4. 将各 sink 接到应用组件(WebRTC / LCD / AI / 录像等)

## 故障排除

### 采集启动失败

- 确认摄像头与音频 ADC 设备初始化成功
- 确认 CSI 摄像头硬件与板型选择(`idf.py bmgr -b ...`)
- 若输入为 1080p,请在 menuconfig 中启用对应传感器模式(例如 SC2336 1920×1080)

### Sink setup 失败 / static assert

- 减少 `CAPTURE_SINKS_SETTINGS` 中的 sink 数量,或增大 `CONFIG_ESP_CAPTURE_MAX_SINK_NUM`
- 确认请求的格式被板级编码器 / PPA 路径支持

### 帧校验失败(`bad` 计数增加)

- **H.264** — 确认输出为 Annex-B(含起始码)
- **JPEG** — 确认完整帧:`FF D8` … `FF D9`
- **RGB565** — 确认长度等于 `width × height × 2`

### JPEG one-shot 超时

- 确认 sink 1 使用 `ESP_CAPTURE_RUN_MODE_ONESHOT` 开启
- 等待抓拍时继续 drain 推流 sink,避免管线反压
- 首次打开抓拍路径时,为 2K 放大 + JPEG 编码预留足够时间

## 技术支持

- 技术支持论坛:[esp32.com](https://esp32.com/viewforum.php?f=20)
- 问题反馈与功能建议:[GitHub issue](https://github.com/espressif/esp-gmf/issues)

To create a project from this example, run:

idf.py create-project-from-example "espressif/esp_capture=1.0.5:capture_multiple_sink"

or download archive (~14.81 KB)