屏幕镜像 (screen_mirror)
屏幕镜像(screen_mirror)模块是 AC792 SDK 中负责通过 TCP/UDP 网络接收远端 JPEG 视频流并在本地 LCD 上实时显示的组件。它以内嵌数据源插件(scr0)配合 jpeg_dec / rep / imc / disp 解码显示流水线的方式,将手机、PC 等设备推送的屏幕画面镜像到开发板屏幕,支持帧率/回包两种流控策略与统计监测。
Purpose and Scope
本文档系统性地说明 screen_mirror 子系统的完整实现,包括:
screen_mirror_api:面向应用层暴露的初始化/反初始化/状态查询接口(net_scr_init/net_scr_uninit/get_net_scr_status);screen_mirror_adapter:作为 pipeline 数据源插件scr0的收包、JPEG 分包重组、线程调度与统计逻辑;- 网络协议细节(
frm_head帧头、分片偏移、校验码)、TCP/UDP 双协议支持、ack 流控模式; - 配置结构体
__NET_SCR_CFG的每个字段含义与默认行为。
以下主题不属于本页范围,请参考对应目录页:LCD 显示与合成(disp/imc 插件)、JPEG 解码器(jpeg_dec)、视频采集/编码(wifi_camera 方案)、通用 pipeline 框架(pipeline_core)。
概述
屏幕镜像本质上是一条“网络 JPEG 流 → 解码 → 缩放 → 格式转换 → 合成显示”的实时视频链路。设备端作为 TCP 服务端(或 UDP 接收端)监听固定端口 NET_SCR_PORT,接收对端发来的 JPEG 帧;每帧可能被拆分为多个分片(slice),由适配层依据帧头中的 seq/offset/frm_sz 重组为完整 JPEG 数据后送入解码显示流水线。
整个模块由 CONFIG_NET_SCR 编译宏控制,未定义该宏时相关代码不会编译进固件。模块设计上复用了 SDK 的 pipeline 插件框架:screen_mirror_api.c 只负责搭链与参数下发,真正收包逻辑在 screen_mirror_adapter.c 的 scr0 插件中,二者通过 pipeline 参数(PIPELINE_SCR_CLI_ADR 等)和全局句柄 g_scr_used 解耦。
关键设计决策:
- 协议无关的显示链路:网络差异被封装在
scr0数据源插件内,解码/显示部分与传输层完全无关; - 两种流控模式:
ack=0以本地解码帧率为准(推流端按 fps 发送),ack=1以回包为准(每收一帧回发 ACK,推流端等 ACK 再发下一帧),适配不同网络质量; - 单通道设计:
SCR_CHANNEL_MAX = 1,同一时刻仅支持一路屏幕镜像,保证 LCD 显示资源独占。
架构
flowchart TD
subgraph sg_Remote["对端设备 (推流端)"]
Sender["手机/PC 屏幕采集 + JPEG 编码"]
Encoder["TCP/UDP 发送"]
end
subgraph sg_App["应用层 (App)"]
App["业务 App 代码"]
API["screen_mirror_api.c<br/>net_scr_init / net_scr_uninit"]
end
subgraph sg_Pipeline["Pipeline 解码显示链路"]
SRC["scr0 数据源插件<br/>screen_mirror_adapter.c"]
JD["jpeg_dec 插件"]
REP["rep 缩放插件"]
IMC["imc 格式转换插件"]
DISP["disp 显示插件"]
end
subgraph sg_Net["网络层 (sock_api)"]
SOCK["sock_reg / bind / listen<br/>SOCK_STREAM / SOCK_DGRAM"]
end
subgraph sg_Hw["硬件"]
LCD["LCD 屏幕"]
NET["WiFi / 以太网"]
end
Sender --> Encoder --> NET --> SOCK
SOCK --> SRC
SRC -->|"JPEG 帧 (重组后)"| JD --> REP --> IMC --> DISP --> LCD
App --> API
API -->|"pipeline_filter_add / link / prepare / start"| SRC
API -->|"PIPELINE_SCR_CLI_ADR / SOCK_TYPE / ACK_CALLBACK"| SOCK
API -->|"PIPELINE_SET_FORMAT"| JD
SRC -->|"ack_cb 回包"| SOCK
上图展示了屏幕镜像的两条关键链路:
- 数据链路(右侧):对端通过 WiFi/以太网将 JPEG 流发送到设备绑定的
NET_SCR_PORT端口,scr0插件收包并重组出完整 JPEG 帧,随后依次经过jpeg_dec(解码为 YUV)、rep(缩放)、imc(格式转换/裁剪)、disp(合成显示到 LCD); - 控制链路(左侧):应用通过
net_scr_init传入__NET_SCR_CFG,API 层创建 pipeline、按名称添加scr0源插件并依次 link 各滤波器,再把网络参数(客户端地址、socket 类型、ack 回调)经 pipeline 参数下发到scr0插件。
该分层刻意将网络细节与显示细节隔离:scr0 只关心“从 socket 拿到完整 JPEG”,disp 只关心“把 YUV 帧显示出来”,任何一层替换(例如把 JPEG 换成 H.264 源)都不影响另一层。
主要实现分析
API 层:net_scr_init 的搭链流程
net_scr_init(screen_mirror_api.c)是屏幕镜像的入口,其执行顺序严格遵循 pipeline 生命周期:初始化 → 添加滤波器 → 设置参数 → 链接 → prepare → start。
int net_scr_init(struct __NET_SCR_CFG *cfg)
{
pipe_filter_t *source_filter, *jpeg_dec_filter, *imc_filter, *rep_filter, *disp_filter;
struct video_format f = {0};
if (__this->state) {
log_error("%s multiple init\n", __func__);
return 0;
}
memcpy(&__this->cli_addr, &cfg->cli_addr, sizeof(struct sockaddr_in));
log_info("scr size: %d x %d", cfg->src_w, cfg->src_h);
log_info("scr fps: %d, prot: %s, ack: %d", cfg->fps, (cfg->prot == 0) ? "tcp" : "udp", cfg->ack);
__this->pipe_core = pipeline_init(NULL, NULL);
ASSERT(__this->pipe_core);
char *source_name = "scr0";
__this->pipe_core->channel = plugin_source_to_channel(source_name);
source_filter = pipeline_filter_add(__this->pipe_core, source_name);
jpeg_dec_filter = pipeline_filter_add(__this->pipe_core, plugin_factory_find("jpeg_dec"));
rep_filter = pipeline_filter_add(__this->pipe_core, plugin_factory_find("rep"));
imc_filter = pipeline_filter_add(__this->pipe_core, find_use_for_display_plugin("imc"));
disp_filter = pipeline_filter_add(__this->pipe_core, plugin_factory_find("disp"));
...
}
Source: screen_mirror_api.c
设计要点:
- 重复初始化防护:
__this->state为 1 时直接返回 0,避免同一 pipe 被重复创建导致资源泄漏; - 源插件按名字查找:
pipeline_filter_add(pipe, "scr0")走插件名注册表,scr0即screen_mirror_adapter.c注册的数据源插件;plugin_source_to_channel(source_name)把源名解析为 channel 号; - imc 插件选择:
find_use_for_display_plugin("imc")而非直接plugin_factory_find,说明 imc 有多个变体(如 DMA2D 加速版),按当前显示配置动态挑选可用实现; - 错误处理为硬断言:
ASSERT(__this->pipe_core)在内存不足时直接停机,适合嵌入式实时场景,避免悬空指针继续执行。
参数下发与格式配置
初始化后半段通过 pipeline_param_set 下发视频格式与网络参数:
//数据源数据格式
f.src_width = cfg->src_w;
f.src_height = cfg->src_h;
//imc不做帧率控制,以下发帧率为准
f.fps = cfg->fps;
//显示配置
f.win.left = 0;
f.win.top = 0;
f.win.width = f.src_width;
f.win.height = f.src_height;
f.win.combine = 1; //合成显示
pipeline_param_set(__this->pipe_core, NULL, PIPELINE_SET_FORMAT, &f);
pipeline_param_set(__this->pipe_core, NULL, PIPELINE_SCR_CLI_ADR, &cfg->cli_addr);
int sock_type = (cfg->prot == 0) ? SOCK_STREAM : SOCK_DGRAM;
pipeline_param_set(__this->pipe_core, NULL, PIPELINE_SCR_SOCK_TYPE, &sock_type);
pipeline_param_set(__this->pipe_core, NULL, PIPELINE_SCR_ACK_CALLBACK, &cfg->ack_cb);
int line_cnt = 16;
pipeline_param_set(__this->pipe_core, NULL, PIPELINE_SET_BUFFER_LINE, (int)&line_cnt);
Source: screen_mirror_api.c
含义与设计意图:
f.win.combine = 1声明该视频窗口参与合成显示(overlay),可与其他摄像头/UI 窗口叠加,而不是独占屏幕;PIPELINE_SCR_*系列参数是屏幕镜像模块自定义的私有参数,由scr0插件在scr_prepare/scr_handle_param中消费——这正是 API 层与适配层解耦的接口契约;PIPELINE_SET_BUFFER_LINE = 16控制解码输出的缓冲行数,直接影响内存占用与延迟(行数越少延迟越低,但解码吞吐受限);fps被直接写入video_format,注释明确“imc 不做帧率控制,以下发帧率为准”,即显示帧率完全由推流端节奏决定。
适配层:scr0 插件与收包状态机
screen_mirror_adapter.c 注册了名为 scr0 的数据源插件。其核心私有数据结构 struct scr_handle 覆盖了单路镜像的全部运行状态:
struct scr_handle {
u8 channel;
u8 ref;
u8 state;
spinlock_t lock;
int socket_type;
void (*ack_cb)(int);
void *sock_hdl;
void *cli_sock_hdl;
u8 sock_fps;
u8 disp_fps;
u32 seq;
struct sockaddr_in cli_addr;
int src_width;
int src_height;
u8 fps;
u32 old_frame_seq;
u32 jpg_size;
int pid; // 收包线程
};
Source: screen_mirror_adapter.c
该句柄的生命周期由插件状态机驱动:PLUGIN_UNINIT → PLUGIN_INITED → PLUGIN_READY → (运行) → PLUGIN_UNINIT。scr_init 中通过 check_channel_legal 从插件名末尾解析 channel 号(如 scr0 → 0),并校验不超过 SCR_CHANNEL_MAX(1),不合法直接 ASSERT——从插件命名规范上杜绝了多路镜像同时启动的可能。
全局句柄表 g_scr_used[SCR_CHANNEL_MAX] 保证同一 channel 只分配一次内存,重复 init 时复用既有句柄。
网络初始化:TCP 服务端 / UDP 接收端
socket_type 由 API 层根据 cfg->prot 换算(0→SOCK_STREAM,1→SOCK_DGRAM),插件在 scr_prepare 阶段调用 net_scr_sock_init 完成绑定:
hdl->sock_hdl = sock_reg(AF_INET, hdl->socket_type, 0, NULL, NULL);
if (hdl->sock_hdl == NULL) {
log_error("socket reg failed.");
return -1;
}
if (sock_set_reuseaddr(hdl->sock_hdl)) {
log_error("socket reuseaddr failed.");
sock_unreg(hdl->sock_hdl);
hdl->sock_hdl = NULL;
return -1;
}
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(NET_SCR_PORT);
ret = sock_bind(hdl->sock_hdl, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
...
if (hdl->socket_type == SOCK_STREAM) {
ret = sock_listen(hdl->sock_hdl, 0xFF);
...
}
Source: screen_mirror_adapter.c
要点:
- 绑定
INADDR_ANY+NET_SCR_PORT,对推流端地址不设限(cli_addr仅在反初始化时用于校验,见下文); sock_set_reuseaddr允许快速重启时复用 TIME_WAIT 端口,避免异常退出后短时间内无法重新绑定;- TCP 模式下
sock_listen(hdl, 0xFF)设置全连接队列深度 255;UDP 模式跳过 listen; - 任何一步失败都会
sock_unreg回滚并返回 -1,scr_prepare收到非零后free(hdl)并终止,保证失败路径不留半初始化资源。
核心流程
端到端数据流时序
sequenceDiagram
participant App as 应用层
participant API as screen_mirror_api
participant SRC as scr0 插件
participant SOCK as sock_api
participant Sender as 推流端 (TCP/UDP)
participant PL as jpeg_dec/rep/imc/disp
App->>API: net_scr_init(&cfg)
API->>API: pipeline_init + filter_add(scr0/jpeg_dec/rep/imc/disp)
API->>API: pipeline_param_set(PIPELINE_SET_FORMAT / SCR_*)
API->>API: pipeline_filter_link × 4
API->>API: pipeline_prepare / pipeline_start
API->>SRC: scr_prepare → net_scr_sock_init(bind NET_SCR_PORT)
SRC->>SRC: 创建收包线程 scr_recv
API-->>App: 返回 0
Sender->>SOCK: 连接/发送 JPEG 分片
SOCK->>SRC: 收包线程 recv
SRC->>SRC: get_jpg_packet 解析 frm_head<br/>按 seq/offset 重组完整 JPEG
SRC->>PL: 送入解码显示链路
PL-->>SRC: 帧消费完成
alt ack=1 (以回包为准)
SRC->>SOCK: 调用 ack_cb 回包 ACK
SOCK-->>Sender: ACK
Sender->>SOCK: 发送下一帧
else ack=0 (以帧率为准)
Sender->>SOCK: 按 cfg->fps 持续推流
end
App->>API: net_scr_uninit(&cfg)
API->>API: 校验 cli_addr (跳过 sin_family/port 后 6 字节)
API->>SRC: pipeline_stop / reset / uninit
API-->>App: 返回 0
时序图中值得注意的细节:
- 地址校验技巧:
net_scr_uninit用memcmp((char *)&__this->cli_addr + 2, (char *)&cfg->cli_addr + 2, 6)比较,跳过前 2 字节(sin_family的 2 字节 + 端口高字节起始),实际比较的是端口(2) + 地址(4),防止不同会话误关别人的镜像通道; - 收包线程:
scr_recv(SCR_THREAD_TASK_NAME)由插件在启动时创建,pid字段保存线程 id,是数据面唯一的并发点; - 回包模式:仅
ack=1时收完一帧立即触发ack_cb,由应用层负责把 ACK 写回 socket 或交给推流协议处理——适配层只做“通知”,不做网络写回,保持职责单一。
JPEG 分片重组协议
推流端可能将一帧 JPEG 拆成多个 UDP/TCP 分片发送,每个分片携带 struct frm_head 帧头。get_jpg_packet 负责把分片还原为整帧:
do {
struct frm_head *head_info = (struct frm_head *)(buf + position);
frame_type = head_info->type & 0x7F;
cur_frame_seq = head_info->seq;
frame_offset = head_info->offset;
slice_data_len = head_info->payload_size;
frame_size = head_info->frm_sz;
...
Source: screen_mirror_adapter.c
帧头字段语义:
| 字段 | 含义 |
|---|---|
type | 帧类型(低 7 位有效,高位可能携带结束/丢弃标志位) |
seq | 帧序号,用于乱序识别与丢帧检测 |
offset | 该分片在整帧内的字节偏移,用于拼装 |
payload_size | 本分片有效载荷长度 |
frm_sz | 整帧 JPEG 总大小 |
重组策略要点:
- 完整性校验:重组前先扫描整包中的
CHECK_CODE(0x88)×CHECK_CODE_NUM(32)校验区,确认数据来自约定的推流协议(bytecmp逐字节比对); - 乱序容忍:UDP 场景下分片可能乱序到达,重组逻辑以
offset为索引写入jpg缓冲区,并以seq判断新旧帧,old_frame_seq记录上一帧序号用于丢帧统计; - 缓冲上限:
JPG_BUF_MAX_SIZE = 250 * 1024(约 244KB),超限分片会被丢弃,防止内存被恶意大帧撑爆; - UDP 单包上限:
UDP_MAX_RECV = 1472字节(以太网 MTU 1500 减去 IP/UDP 头),接收缓冲区按单包上限静态分配,避免堆上频繁 malloc。
运行统计
模块内置 struct scr_state 统计运行期指标:
struct scr_state {
u8 fps_min;
u8 fps_max;
u32 fps_sum;
float jpg_sz_min; //KB
float jpg_sz_max;
u32 jpg_sz_sum;
u32 cnt;
};
Source: screen_mirror_adapter.c
统计对象包括实际接收帧率(min/max/均值)与每帧 JPEG 大小(min/max/累计,单位 KB),cnt 为累计帧数。这些数据可用于调试网络质量、评估压缩率,是 Wi-Fi 屏镜像场景定位“卡顿/模糊”问题的第一手依据。
使用示例
公共 API 定义
屏幕镜像对外暴露的最小接口集合,定义在头文件中:
struct __JPG_INFO {
u32 src_w;
u32 src_h;
u32 buf_len;
u8 buf[];
};
struct __NET_SCR_CFG {
struct sockaddr_in cli_addr;
int fps;
int src_w;
int src_h;
int prot; //prot:协议类型 0->tcp 1->udp
int ack; //ack:规则类型 0->以帧率为准 1->以回包为准
void *ack_cb; //回包回调函数
};
u8 get_net_scr_status(void);
int net_scr_init(struct __NET_SCR_CFG *cfg);
int net_scr_uninit(struct __NET_SCR_CFG *cfg);
Source: screen_mirror_api.h
__JPG_INFO 是重组后的完整 JPEG 帧载体(src_w/src_h 为源分辨率,buf 为柔性数组成员);__NET_SCR_CFG 是唯一的配置入口,应用只需填好结构体即可启动镜像。
应用层调用方式(TCP 模式)
以 TCP + 帧率流控为例,应用代码先填写配置再调用初始化:
struct __NET_SCR_CFG cfg = {0};
cfg.cli_addr = ...; // 对端地址(TCP 模式下实际由 accept 获得)
cfg.fps = 30; // 目标帧率
cfg.src_w = 480; // 源宽
cfg.src_h = 854; // 源高
cfg.prot = 0; // 0 -> tcp
cfg.ack = 0; // 0 -> 以帧率为准
cfg.ack_cb = NULL;
if (net_scr_init(&cfg) == 0) {
// 镜像运行中,可用 get_net_scr_status() 查询状态
}
...
net_scr_uninit(&cfg); // 退出前反初始化
Source: screen_mirror_api.h
网络参数换算参考
视频链路中 mirror/rotate 字段的用法可在 wifi_camera 应用中看到对照示例:
req.display.mirror = 0; //1镜像
Source: video_photo.c
/* req.display.mirror = VIDEO_HOR_MIRROR | VIDEO_VER_MIRROR; */
/* req.display.rotate = 90; //90/270 */
Source: video_rec.c
镜像与旋转是在视频显示请求(video_req/video_display_req)中配置的,screen_mirror 链路的 disp 插件同样遵循该显示语义,支持水平/垂直镜像组合与 90°/270° 旋转。
配置选项
编译开关
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
CONFIG_NET_SCR | 宏 | 关闭 | 屏幕镜像模块总开关;未定义时 screen_mirror_api.c 与 screen_mirror_adapter.c 的模块代码整体不参与编译 |
__NET_SCR_CFG 运行期配置
| 字段 | 类型 | 默认/取值 | 说明 |
|---|---|---|---|
cli_addr | struct sockaddr_in | 无 | 推流端地址;net_scr_uninit 用它做会话校验(比较端口+IP 共 6 字节) |
fps | int | 无 | 目标帧率;写入 video_format.fps,imc 不控帧率,实际帧率以推流端为准 |
src_w | int | 无 | 源图像宽,决定显示窗口宽与解码缓冲尺寸 |
src_h | int | 无 | 源图像高,决定显示窗口高 |
prot | int | 0 | 协议类型:0 → TCP(SOCK_STREAM),1 → UDP(SOCK_DGRAM) |
ack | int | 0 | 流控规则:0 → 以帧率为准,1 → 以回包为准 |
ack_cb | void * | NULL | 回包回调函数指针;ack=1 时每收完一帧触发 |
适配层内部常量
| 常量 | 值 | 说明 |
|---|---|---|
NET_SCR_PORT | 由 rt_stream_pkg.h 定义 | 监听端口,TCP/UDP 共用 |
SCR_CHANNEL_MAX | 1 | 最大镜像通道数(单路) |
SCR_THREAD_TASK_NAME | "scr_recv" | 收包线程名 |
JPG_BUF_MAX_SIZE | 250 × 1024 | 单帧 JPEG 重组缓冲上限(字节) |
CHECK_CODE | 0x88 | 协议校验码字节 |
CHECK_CODE_NUM | 32 | 校验码连续字节数 |
UDP_MAX_RECV | 1472 | UDP 单包最大接收长度(MTU 预算内) |
PIPELINE_SET_BUFFER_LINE | 16 | 解码输出缓冲行数 |
API 参考
int net_scr_init(struct __NET_SCR_CFG *cfg)
初始化屏幕镜像:创建 pipeline、添加并链接 scr0/jpeg_dec/rep/imc/disp 滤波器、下发格式与网络参数、prepare 并 start。
参数:
cfg(struct __NET_SCR_CFG *):镜像配置,含地址、fps、分辨率、协议与流控模式。
返回: 0 成功;失败时可能为 -1(内部错误)或在 ASSERT 处直接停机(如 pipeline_init 返回 NULL)。
注意事项: 重复调用时打印 "multiple init" 并直接返回 0(幂等,不重建)。
int net_scr_uninit(struct __NET_SCR_CFG *cfg)
反初始化:校验会话地址匹配后 pipeline_stop → pipeline_reset → pipeline_uninit。
参数:
cfg(struct __NET_SCR_CFG *):需与 init 时地址一致。
返回: 0 成功;-1 表示未在运行或 cli_addr 不匹配(打印 "cli addr not match.")。
u8 get_net_scr_status(void)
返回: 1 表示镜像通道运行中,0 表示未启动/已停止。常用于业务轮询或退出条件判断。
失败模式、边界情况与并发
重复初始化与并发保护
net_scr_init通过__this->state防止重复初始化;适配层通过hdl->state != PLUGIN_UNINIT跳过重复scr_init,并通过全局句柄表g_scr_used[channel]保证单通道单实例。- 收包线程
scr_recv与主控线程通过spinlock_t lock保护句柄内共享字段(如seq、sock_fps),get_jpg_packet的重组缓冲区(total_payload_len/finish)为线程局部静态变量,天然避免跨线程竞争,但代价是同一时刻只能有一个收包线程在重组——与SCR_CHANNEL_MAX = 1的约束一致。 - 反初始化路径:
net_scr_uninit先清state再pipeline_stop,保证收包线程在 pipeline 停止前有机会退出,避免 use-after-free。
推流端异常场景
| 场景 | 行为 |
|---|---|
| 推流端中途断开(TCP) | 收包返回错误/0,get_jpg_packet 打印 recv len err,重组状态可能残留;上层应调用 net_scr_uninit 复位后重新 init |
分片包小于 sizeof(struct frm_head) | 直接返回 -1 丢弃,防越界解析 |
帧数据超过 JPG_BUF_MAX_SIZE | 超出部分截断/丢弃,防止大帧撑爆静态缓冲 |
| UDP 乱序/丢包 | 以 seq + offset 重组,丢帧由 old_frame_seq 检测并计入统计 |
| 非约定推流端(校验码不符) | bytecmp 比对 0x88 × 32 失败,整包被拒绝 |
已知限制
- 单通道设计(
SCR_CHANNEL_MAX=1)不支持多路镜像同时显示; cli_addr仅用于 uninit 校验,TCP 模式的实际对端由accept决定,若多客户端连接,仅首路生效;- 掉电/异常复位时若未调用
net_scr_uninit,靠sock_set_reuseaddr缓解端口复用问题。
性能与运维考虑
- 延迟预算:
PIPELINE_SET_BUFFER_LINE = 16限制解码缓冲行数,配合即时显示策略压低端到端延迟——这是屏镜像体验(触控跟手性)的关键参数,调大可提升解码容错但增加延迟; - 内存占用:单帧 JPEG 缓冲 250KB(静态区)+ UDP 收包缓冲 1472B(静态) + 解码显示链路各滤波器缓冲,镜像分辨率与
JPG_BUF_MAX_SIZE需按可用 RAM 评估; - UDP 模式 MTU 约束:单包 1472B 保证不触发 IP 分片,减少重组开销,但推流端需自行分片(
frm_head.offset机制正是为此设计); - 可观测性:
struct scr_state的 fps 与 JPEG 大小统计(min/max/均值)可通过调试日志输出,用于评估网络带宽与压缩质量;fps_sum/jpg_sz_sum为 32 位累计值,长时间运行时需留意溢出(约 1.7 亿帧/字节级)后再取均值; - 日志开关:
LOG_TAG_CONST分别定义为SCREEN_MIRROR_API与SCREEN_MIRROR_ADAPTER,且LOG_ERROR_ENABLE/LOG_INFO_ENABLE/LOG_DUMP_ENABLE均已打开,产线固件可裁剪以省 flash/串口带宽。
扩展点
- 替换/新增数据源协议:
scr0插件严格遵循 pipeline 源插件接口(scr_init/scr_prepare/scr_connect+ 参数回调),可通过注册新插件名(如scr1)并修改check_channel_legal的SCR_CHANNEL_MAX扩展多路镜像; - 自定义回包协议:
ack_cb是应用层钩子,ACK 帧格式完全由推流协议决定,适配层不关心内容,可对接私有握手/鉴权协议; - 显示链路替换:
find_use_for_display_plugin("imc")允许按硬件能力选择 imc 变体(如 DMA2D GPU 加速版),解码/显示插件可通过plugin_factory_find替换为硬解实现; - 统计上报:在
scr_state更新处挂接日志或事件回调,即可实现帧率/码率的实时监控上报。
相关链接
- screen_mirror_api.h — 公共接口与配置结构体定义
- screen_mirror_api.c — pipeline 搭建与参数下发实现
- screen_mirror_adapter.c — scr0 数据源插件、收包重组与统计
- rt_stream_pkg.h —
frm_head帧头与NET_SCR_PORT等流协议定义 - video_dec_server.h — 视频解码服务与 mirror/rotate 显示参数
- 相关目录页:LCD 显示合成(
disp/imc)、JPEG 解码(jpeg_dec)、Pipeline 框架(pipeline_core)、WiFi 摄像头方案(wifi_camera)