kevincoooool/transport_webrtc

2.0.0

Latest
uploaded 1 hour ago
Portable G711A WebRTC audio/video transport with pluggable signaling, audio, video, network and ICE adapters

Readme

# transport_webrtc

面向 ESP-IDF 的单会话 WebRTC 音视频传输组件。它封装 PeerConnection、ICE、DTLS-SRTP、G.711A 双向语音和可选的 H.264/MJPEG 编码视频帧。公开头文件只使用组件自己的类型,目标工程无需包含或理解 `esp_peer` API。

组件负责媒体会话,应用负责网络连接、登录鉴权、呼叫状态机、按钮和 UI。当前实现一次只运行一条通话,会话之间可重复 `prepare → negotiate → start → stop`。

## 能力与边界

- 8 kHz PCMA WebRTC 音频;板级 PCM 可选 8 kHz 或 16 kHz、单声道 signed int16、20 ms 一帧。
- 可选 H.264 Annex-B 或 MJPEG 编码视频。摄像头、编解码器和显示由应用提供。
- Room、Front desk、Waiter 三个默认配置;`cfg.role` 决定默认 offerer/answerer,也可在每次会话前用 `transport_webrtc_set_offerer()` 覆盖。
- 两种信令接入方式:协议无关的原始 SDP/ICE 回调,以及兼容现有工程的内置 JSON 编解码。
- Kconfig 静态 STUN/TURN、运行时 STUN/TURN 注入,以及可选的 HTTPS 临时凭证获取。
- 可注入网络 acquire/release 钩子,适配 Wi-Fi、以太网、PPP 或多网卡路由。
- 音频 HAL 自己负责声卡、多通道拆分、AEC、NS、AGC 和功放控制。组件不绑定 ESP-SR 或特定 codec。

组件依赖 ESP-IDF 5.5.x、`espressif/esp_peer 1.5.5` 和 `kevincoooool/media_lib_utils ~0.9.0`。这些依赖全部是私有依赖,不会泄漏到使用方的公开接口。

## 放入其他工程

将整个目录复制到目标工程:

```text
your_project/
├── components/
│   └── transport_webrtc/   <- 本目录
├── main/
└── CMakeLists.txt
```

保留组件自己的 `idf_component.yml`。在目标工程的组件依赖中加入:

```cmake
idf_component_register(
    SRCS "app_main.c" "my_webrtc_port.c"
    INCLUDE_DIRS "."
    REQUIRES transport_webrtc
)
```

随后在 `menuconfig → Component config → Portable WebRTC media transport` 配置角色、PCM 速率、ICE、缓存和可选视频。Room 默认生成 offer;Front desk 和 Waiter 默认生成 answer。双方的业务呼叫方向不受此默认值限制。

内存策略由同一菜单中的 `Task stack and non-DMA queue memory` 切换。`Internal RAM` 是兼容性默认值;`PSRAM` 会把三个 WebRTC 任务栈、音频接收队列和待处理 ICE 队列放入 PSRAM,适合内部 RAM 紧张的 UI 工程。PSRAM 选项要求工程同时开启外部任务栈和外部 BSS 支持;声卡 DMA 缓冲不受此选项影响。

## 最小音频接入

HAL 的 `read_pcm` 和 `write_pcm` 会在两个任务中并行调用。它们应传输完整 20 ms 帧,并使用有界超时。组件会复制整个 HAL 结构体,`ctx` 指向的对象须存活到 `transport_webrtc_deinit()`。

```c
#include "transport_webrtc/transport_webrtc.h"

transport_webrtc_config_t cfg;
transport_webrtc_config_default(&cfg);
cfg.audio.ctx = &board_audio;
cfg.audio.codec_prepare = board_audio_prepare;
cfg.audio.codec_start = board_audio_start;
cfg.audio.codec_stop = board_audio_stop;
cfg.audio.amp_enable = board_amp_enable;
cfg.audio.read_pcm = read_pcm;
cfg.audio.write_pcm = write_pcm;
cfg.on_event = on_media_event;
cfg.event_ctx = &app;
ESP_ERROR_CHECK(transport_webrtc_init(&cfg));
```

多通道 ADC 应在 `read_pcm` 内提取真正的近端麦克风。若目标板使用硬件回采做 AEC,应先用 microphone + playback reference 完成 AEC,再把清理后的单声道 PCM 返回给组件。

## 推荐:接入自己的信令协议

原始信令接口适用于 WebSocket、MQTT、HTTP、TCP 或其他控制面。发送回调运行在 WebRTC 任务中,必须复制数据到有界发送队列后立即返回。

```c
static bool send_sdp(const char *sdp, bool offer, uint32_t call_id, void *ctx)
{
    return signaling_enqueue_sdp(ctx, call_id, offer, sdp); /* 必须复制 sdp */
}

static bool send_candidate(const char *candidate, uint32_t call_id, void *ctx)
{
    return signaling_enqueue_ice(ctx, call_id, candidate); /* 必须复制 candidate */
}

cfg.raw_signaling = (transport_webrtc_raw_signaling_t) {
    .ctx = &signaling,
    .send_sdp = send_sdp,
    .send_candidate = send_candidate,
};
```

接受呼叫后先创建本地会话,再投递远端 SDP/ICE:

```c
ESP_ERROR_CHECK(transport_webrtc_prepare(call_id));
ESP_ERROR_CHECK(transport_webrtc_negotiate());
ESP_ERROR_CHECK(transport_webrtc_start());

/* 信令接收任务;只投递当前 call_id 的数据。 */
transport_webrtc_set_remote_description(remote_sdp, remote_is_offer);
transport_webrtc_add_remote_candidate(remote_candidate);
```

`add_remote_candidate()` 可早于远端 SDP,组件会暂存候选。`prepare()` 前到达的业务消息应由应用按 `call_id` 排队。应用控制任务在通话期间每 10–20 ms 调用一次 `transport_webrtc_poll()`;连接、断线和错误事件都从该函数投递。

## 兼容 JSON 信令

不设置 `raw_signaling` 时,可设置 `cfg.signaling_send` 并将收到的一条完整 NUL 结尾 JSON 交给 `transport_webrtc_on_signaling()`。

- 开启 `CONFIG_TRANSPORT_WEBRTC_SIGNAL_SPRING`:使用 `offer` / `answer` / `candidate`、字符串 `callId` 和 `payload`。通话前调用 `transport_webrtc_set_signaling_session()`。`signaling_spring.h` 还提供 register/call/accept/hangup JSON 辅助函数。
- 关闭该选项:使用 Legacy LAN 的 `SDP_OFFER` / `SDP_ANSWER` / `ICE_CANDIDATE` 和 numeric `call_id`。独立示例使用此模式。

内置 JSON 是兼容适配层。新工程优先使用原始信令接口,业务协议无需跟随组件内置格式。

## ICE 与公网

静态部署可直接在 Kconfig 填入 STUN/TURN。短期凭证可在 IDLE 状态下注入;字符串会被组件复制,调用方可立即释放原始存储:

```c
transport_webrtc_ice_server_t servers[] = {
    { .url = "stun:stun.example.com:3478" },
    {
        .url = "turns:turn.example.com:5349",
        .username = turn_user,
        .credential = turn_password,
    },
};
ESP_ERROR_CHECK(transport_webrtc_set_ice_servers(servers, 2, false));
```

最多支持两个 ICE server。`relay_only=true` 会强制只使用 TURN。也可在 IDLE 状态调用 `transport_webrtc_fetch_ice(url)` 获取本工程 Spring 服务返回的临时 TURN 凭证;该调用执行同步 HTTPS GET,应放在控制任务中,并在 `prepare()` 之前完成。

默认不修改系统路由。多网卡工程可提供网络钩子:

```c
cfg.network = (transport_webrtc_network_hal_t) {
    .ctx = &network_route,
    .acquire = call_route_acquire,
    .release = call_route_release,
};
```

`acquire` 在创建 PeerConnection 前调用;成功后,每条成功、失败或超时会话都会调用一次 `release`。当前 86iot-P4 工程可开启 `CONFIG_TRANSPORT_WEBRTC_PIN_WIFI_ROUTE` 保留 Wi-Fi/PPP 兼容逻辑;通用移植应实现上述网络钩子。

## 视频约定

接收回调中的数据只在回调期间有效,应用须复制到有界解码队列。H.264 使用 Annex-B,关键帧携带 SPS/PPS。收到 `TRANSPORT_WEBRTC_EVENT_KEYFRAME_REQUEST` 时应要求编码器产生 IDR。

发送视频时在采集瞬间读取 `transport_webrtc_media_time_ms()`,编码完成后沿用该毫秒 PTS:

```c
int64_t pts = transport_webrtc_media_time_ms();
/* encode captured image */
if (pts >= 0) {
    transport_webrtc_send_video(encoded, encoded_size, (uint32_t)pts);
}
```

## 生命周期和线程规则

1. 网络和板级资源就绪后调用 `transport_webrtc_init()`。
2. 在 IDLE 状态设置 offerer、ICE、HAL 或信令适配。
3. 调用 `prepare(call_id)`,再调用 `negotiate()` 和 `start()`。
4. 通话期间周期调用 `poll()`,信令任务投递远端 SDP/ICE。
5. 挂断调用 `stop()`;应用退出时停止自己的任务后调用 `deinit()`。

`start()` 可以早于 ICE 连接,媒体会在连接后由 `poll()` 启动一次。不要从 ISR、PCM、视频或信令发送回调调用控制 API。事件回调由 `poll()` 在组件锁之外执行,可以安全调用 `stop()` 等控制 API。

## 目录

- `include/transport_webrtc/transport_webrtc.h`:稳定的传输 API。
- `include/transport_webrtc/signaling_spring.h`:可选 Spring 业务 JSON 辅助函数。
- `src/`:Peer、音频、信令、ICE 和 G.711A 私有实现。
- `examples/standalone`:双 ESP Legacy LAN 示例及板级 HAL 模板。
- `tests/check_contracts.py`:Kconfig 矩阵、C 语法和组件边界静态检查。
- `PORTING.md`:移植验收清单和 86iot-P4 AEC 参考实现说明。
- `CALL-LATENCY.md`:公网呼叫建链延迟分解、ICE 拉取时机、优化项与验证方法。

本次 2.0 API 将视频结构体从 `esp_peer_*` 改为 `transport_webrtc_*`,并增加原始信令、运行时 ICE 和网络 HAL。旧工程升级方法见 [MIGRATION.md](MIGRATION.md)。

Links

Supports all targets

To add this component to your project, run:

idf.py add-dependency "kevincoooool/transport_webrtc^2.0.0"

download archive

Stats

  • Archive size
    Archive size ~ 60.56 KB
  • Downloaded in total
    Downloaded in total 0 times
  • Downloaded this version
    This version: 0 times

Badge

kevincoooool/transport_webrtc version: 2.0.0
|