ESP-Media-Service 组件

[English]

ESP-Media-Service 简介

ESP-Media-Service 是乐鑫在 esp_service 之上提供的统一媒体服务层。它把采集、播放、RTSP、RTMP、封装、解封装等能力做成可复用的服务:产品固件只需 创建一次、配置一次、链接一次,之后每个应用、每次场景切换,都只做控制,不再自己搬音视频帧。

基于这一设计,应用开发时主要可以提供以下便利:

  • 服务在设备上 构建一次,门铃预览、本地回放、推流、录像都可以复用同一套 Capture / Player / RTSP / RTMP。

  • 应用和 MCP Agent 只下发 start / stop / link 等控制;重数据始终在服务之间通过 esp_media_service_link 流动,不经过 MCP。

  • 不需要先精通 GMF / ADF 管线细节,也能做出能对接多种 sink 的新媒体服务。

  • 硬件还没到时,可用 Dummy 源 / Dummy 接收端做预开发,量产时再换成真实摄像头、喇叭或屏幕。

统一媒体模型

传统嵌入式多媒体应用容易变成“每个产品各写一套管线”:采集回调里拷贝帧、自己对接 RTSP、播放器再写一遍解码和渲染。换一块板、加一路推流、或让 Agent 自动组应用时,这些胶水代码很难复用。

ESP-Media-Service 把这件事收成统一模型:

  • 源(SRC) 产生媒体帧,例如 Capture、Extractor、RTSP/RTMP 拉流、Dummy 源。

  • 接收端(SINK) 消费媒体帧,例如 Player、RTSP/RTMP 推流、Muxer、Dummy 接收端。

  • 源接收一体(SRC_SINK) 既消费也产生,例如 ChatGPT、OpenAI、AI 检测等适合,既生产数据又消费数据。

应用只通过 esp_media_service 这一层交互:创建、配置、link、start / stop。访问本地文件(Extractor / Muxer / Player URL)和访问网络(RTSP / RTMP)对 APP 是同一套接口。RTSP、RTMP 也是子类:拉流时作为 SRC,推流或 Server 时作为 SINK,并不需要 APP 换一套 API。

APP、esp_media_service 与子类实现的分层关系

简单设计

从上到下分成三层,中间用分割线隔开:

  • APP 层:产品应用或 MCP Agent。只对接 esp_media_service,不直接操作 GMF、编解码或协议状态机。

  • Service 层 ( esp_media_service ):统一生命周期、角色、stream、provider 和 link。文件源和网络源对 APP 看起来一样。

  • 子类实现:Capture、Extractor、Player、Muxer、Dummy,以及可兼 SRC/SINK 的 RTSP、RTMP。

一个服务实例管理一组 stream ;一个 stream 包含一组 track (音频、视频等)。默认使用 ESP_MEDIA_DEFAULT_STREAM (stream 0)即可完成大多数单路产品。

链接时的内部顺序对应用是透明的:校验角色 → sink 提出 request → source 接受 request → source 给出 provider → sink 保存 provider。之后 sink 读帧、source 写帧。RTMP 这类需要音视频交错的协议,可在 link 时请求 global cache,由源侧自动适配。

如何拼出一条服务管线

无论最终接到播放器、RTSP 还是文件,产品侧的步骤都一样:

创建、配置、链接、启动

典型控制代码只有链接和启动,媒体帧不会出现在应用循环里:

esp_media_stream_id_t stream = ESP_MEDIA_DEFAULT_STREAM;

esp_media_service_link(src_service, stream, sink_service, stream);

esp_service_start(sink_service);  /* 先启动消费端 */
esp_service_start(src_service);

约定:

  • 根据实际需求决定 先启动 sink,再启动 source, 避免丢帧,还是立刻出图。

  • 停止时通过相互通知对端机制,做到无顺序依赖,不卡死 (一般约定生产者持有资源生存期更长一点)。

  • Agent / 应用只改变“谁连谁、是否运行”;帧始终走设备内 link。

一次构建,处处复用

固件里把常用服务注册进 esp_service_manager 后,不同产品形态只是 换链接,而不是重写媒体栈:

任意 SRC 链接到兼容 SINK

例如同一路 Capture:

  • 链到 Player:本地预览或对讲回放。

  • 链到 RTSP Server / Pusher:手机或 NVR 拉流。

  • 链到 RTMP Pusher:推到直播平台。

  • 链到 Muxer:写 SD 卡 MP4 / TS / FLV。

  • 链到 Dummy SINK:先验证帧率和数据量。

同一套 Player 也可以接收 Extractor(本地/HTTP 文件)、RTSP 拉流、RTMP 拉流或 Capture。这就是“服务构建一次、每个 APP 都用”:APP 差异在场景编排,不在底层编解码细节。

现成服务一览

采集服务

  • esp_capture_service:通用采集 SRC,封装 esp_capture,不绑定具体板级外设。适合自定义摄像头源、叠加层等。

  • esp_audio_capture_service:板级录音封装,通过 esp_board_manager 发现 ADC;可启用 AEC / VAD / WakeNet 等 AI 前端。

  • esp_video_capture_service:板级音视频录像,发现摄像头并复用音频采集;支持多 stream、音画同步、存储 muxer。

典型便利:声明式 setup(源 + track + 可选 muxer)一次应用;运行时可启停 stream / track、one-shot 抓拍、手动或自动录像。下游用 esp_media_service_link 拿走帧,不必手写采集回调。

播放服务

  • esp_player_service:媒体 SINK。一个实例对应一块板子上的播放出口,上面可以有多路 stream(影片、BGM、TTS)。

  • 板级产品使用 esp_audio_player_service 或 esp_video_player_service,自动接 DAC / LCD。

每路输入三选一:URL(文件 / HTTP / HLS)、应用 feed 帧、或 零拷贝 media link。与任意 SRC 级联时,应用不要再 write_frame。还提供混音、优先级抢占、播放列表和音视频同步。

RTSP 服务

esp_rtsp_service 是 esp_rtsp 的高层封装,一个实例一种角色:

  • Server:把链接进来的基本音视频帧提供给远端客户端(如 VLC / ffplay)。当前一个实例服务一个拉流端。

  • Pusher(SINK):把链接的帧推到 RTSP 服务器。

  • Puller(SRC):拉 RTSP URL,再 link 到 Player 或 Dummy。

应用不处理会话状态机和 RTP 组包。SINK / SERVER 启动时从上游 provider 读取轨道和编解码信息。

RTMP 服务

esp_rtmp_service 覆盖 RTMP / RTMPS:

  • Pusher(SINK):推到直播服务器(如自建或平台收流地址)。

  • Puller(SRC):从直播 URL 拉流后交给 Player 等 sink。

  • Server:本地收流与转发;该角色 没有 esp_media_service_link 数据通路。

SINK / SRC 传输的是基本音视频帧,不是 FLV 文件字节。不要把 Muxer 的封装字节送进 RTMP;录像和推流应共享同一路上游 SRC。交错音视频会在 link 时请求全局缓存。

其它可链接服务

  • esp_muxer_service:把已编码帧封装进容器,写文件、出流式字节,或两者同时做。

  • esp_extractor_service:从文件 / HTTP / HLS / 内存缓冲解出基本帧,作为 SRC 接到任意 sink。

  • esp_media_dummy_service:无硬件时的源或接收端,见下一节。

没有硬件时用 Dummy 预开发

真实摄像头、麦克风、LCD 往往晚于协议和产品逻辑就绪。Dummy 服务用同一套 link API 补上缺口:

Dummy 源与 Dummy 接收端替换真实硬件

启用方式(Kconfig):

  • CONFIG_ESP_MEDIA_DUMMY_SERVICE_SRC_SUPPORT:虚拟源,输出测试图案。

  • CONFIG_ESP_MEDIA_DUMMY_SERVICE_SINK_SUPPORT:虚拟接收端,消费帧并统计。

常见用法:

  • Dummy SRC:替代摄像头 / 麦克风,先打通编码、封装、RTSP/RTMP 推流和 Agent 编排。

  • Dummy SINK:替代显示 / 喇叭,用 esp_media_dummy_service_get_stats() 确认帧和字节是否在流动。

  • 板卡到位后,只替换 SRC/SINK 实例,链接代码保持不变。

图案编解码(以组件当前实现为准):

  • 音频:PCM、AAC、OPUS、MP3

  • 视频:H.264、MJPEG、RGB565、RGB888、YUV420

编码轨在 add_track() 时通常只需填写 codec;原始 PCM / 原始视频需要完整 track 元数据。

给 MCP Agent 编排应用

esp_service 可把已注册服务的 JSON 工具暴露给 MCP(HTTP / SSE / WebSocket / UART / STDIO / SDIO)。Agent 因此可以在 已有服务 上组应用,而不是在设备上重写媒体栈。

媒体层约定:

  • esp_media_service_link / unlink 按 服务名 解析,绑定一次 esp_media_service_mcp_register(mgr)。

  • Dummy sink 另有 start / stop / get_stats,便于 PC 侧验证链路。

  • Capture、RTSP、RTMP、板级 Player 等各自提供 setup / URL / 录制等控制工具。

  • 媒体帧不会经过 MCP。 Agent 只做控制;重数据在 C 语言 link 上走。

这对产品的意义是:量产固件预置 Capture + Player + RTSP + Dummy,现场或产线 Agent 只需“把 capture 链到 rtsp 并 start”,即可得到预览流;改链到 muxer 即变成录像机。控制面可脚本化,数据面仍保持嵌入式性能。

如何设计一个服务

新产品功能(例如 AI 检测、OSD 叠加、自定义网络 sink)不必先吃透 GMF element、codec 表和线程模型。按媒体合同实现即可对接已有 Capture / Player / RTSP:

  1. 把 esp_media_service_t 作为结构体第一个成员嵌入。

  2. 声明角色:SRC / SINK / SRC_SINK。

  3. 源实现 get_provider() (推荐默认用 esp_media_track_mngr_t 排队写帧);接收端实现 set_provider() 并循环 acquire_frame / release_frame。

  4. 可选实现 get_request / set_request,让对端在 link 时自动适配(例如 RTMP 的 global cache)。

  5. 生命周期仍走 esp_service_start / stop。

源侧默认写法示意:

esp_media_track_mngr_cfg_t cfg = { .max_track_num = 2 };
esp_media_track_mngr_create(&cfg, &svc->mngr);
esp_media_track_mngr_add_track(svc->mngr, &audio_track);
esp_media_track_mngr_add_track(svc->mngr, &video_track);
esp_media_track_mngr_get_provider(svc->mngr, &svc->provider);
/* 生产任务里 esp_media_track_write_frame();get_provider 返回该 provider */

这样做的效果是 一次开发、自动适配多种 sink:只要对端也是 esp_media_service,你的检测服务可以先链 Dummy 验证,再链 Player 预览,再链 RTSP 给手机看,而无需为每个出口各写一套帧回调。

典型产品场景

智能门铃 / 猫眼

Capture 同时服务本地预览和远端查看:一路 link 到 Player(门口屏),一路 link 到 RTSP Server。事件触发时再 link Muxer 写短视频。硬件未到时用 Dummy SRC 先调手机拉流。

摄像头直播与录像

同一 Capture SRC:RTMP Pusher 推到直播平台,Muxer 在 SD 卡切片录像。不要把 Muxer 输出再喂给 RTMP。Agent 可按“仅预览 / 仅录像 / 边看边录”切换链接。

可视对讲 / 室内屏

RTSP 或 RTMP Puller 作为 SRC,link 到 Video Player。TTS 或提示音走 Player 的另一路 stream,利用混音和优先级,避免打断门口视频。

本地媒体与网络电台

Extractor 打开 file:// 或 HTTP/HLS,link 到 Audio/Video Player。与 Capture 预览共用同一播放服务实例,只换 SRC。

无屏产测与 CI

Capture 或协议 SRC 链到 Dummy SINK,读 get_stats 判断是否出帧。适合产线、夜间回归和 Agent 自动验证,不必接 LCD。

自定义 AI 媒体节点

做一个 SRC_SINK 服务:从 Capture 取帧做检测,再写出叠加后的帧。下游仍是标准 Player / RTSP。媒体框架细节留在 Capture 与 Player 里,AI 服务只面对 track / provider。

Agent 现场组应用

设备出厂带齐服务。安装工或 Agent 通过 UART/Wi-Fi MCP 执行:发现服务名 → link capture 到 rtsp → start。换项目时改链接,不必重新烧写媒体核心。

设计建议

  • 板级产品优先用 esp_audio_capture_service / esp_video_capture_service 和音视频 Player 子类,让 Board Manager 接好外设。

  • 自定义源、叠加、无板级绑定的采集,再下沉到 esp_capture_service。

  • 单路预览用 default stream 即可;BGM + 影片、主码流 + 子码流再用多 stream。

  • RGB/DPI 预览、高分辨率推流、多 sink 同时消费时,结合 PSRAM、缓存模式和实际 FPS 做端到端测试。

  • 启用文本或复杂 UI 时,预览路径走 Video Player / Video Render,不要在应用里拆帧自己画。

  • MCP 仅用于编排;不要尝试把视频帧塞进工具调用。

FAQ

Q: 应用还需要自己拷贝音视频帧吗?

A: 链接场景下不需要。esp_media_service_link 之后,帧由源写入、接收端读取。Player 的 URL / feed 路径是另外两种输入,与 link 互斥。

Q: Dummy 和真实 Capture 能混用吗?

A: 可以。例如 Dummy SRC 链 RTSP 先调协议;或真实 Capture 链 Dummy SINK 先看统计。替换时保持相同角色和 stream ID,链接代码可不变。

Q: RTMP Server 为什么不能 link?

A: 该角色负责本地收流与转发,没有 esp_media_service 数据通路。推流/拉流请用 SINK/SRC 角色,并 link 到 Capture 或 Player。

Q: 父类 Player 有没有 MCP?

A: esp_player_service 本身不提供 MCP。产品侧工具在 esp_audio_player_service / esp_video_player_service (对应 Kconfig)。媒体 link/unlink 仍在 esp_media_service MCP。

组件链接