TXW81x 视频对讲开发实战:JPEG over UDP 传输、双通道显示与 LVGL 页面联动
1. 项目概述
本文面向 81x_intercom_ui 工程,重点说明以下两个模块:
sdk/app/video_intercom/:负责 JPEG 视频帧的获取、UDP 分片发送、网络接收、帧重组和远端 JPEG 投递。sdk/lib/gui/81x_demo_ui/:负责启动画面、按键翻页、本地/远端画面模式切换,以及 LCD P0/P1 双视频通道布局。
当前方案采用双端对等结构。每台设备既是发送端,也是接收端:本地摄像头画面编码成 JPEG 后通过 UDP 发给对端,同时接收对端 JPEG 并解码显示。
系统中的两路显示通道约定如下:
| 通道 | 数据来源 | 用途 |
|---|---|---|
LCD P0 / R_VIDEO_P0 |
本地摄像头缩放流 | 本地预览 |
LCD P1 / R_VIDEO_P1 |
网络接收后解码的远端 JPEG | 远端画面 |
2. 总体架构
1 | 本机摄像头 |
这里的 LVGL 主要承担 OSD 和交互控制,不直接绘制实时视频像素。视频由 LCDC 的 P0/P1 硬件通道输出,LVGL 页面使用透明背景叠加在视频层之上。
3. 目录与文件职责
3.1 视频传输模块
| 文件 | 职责 |
|---|---|
sdk/app/video_intercom/video_intercom.c |
UDP socket、发送/接收任务、协议封包、JPEG 分片与重组、远端解码流初始化 |
sdk/app/video_intercom/video_intercom.h |
视频传输控制 API,与 sdk/include/bbm_protocol.h 暴露的接口一致 |
sdk/include/bbm_protocol.h |
UI、音频对讲等模块使用的公共协议头文件 |
3.2 LVGL UI 模块
| 文件 | 职责 |
|---|---|
sdk/lib/gui/81x_demo_ui/81x_demo_ui.c |
UI 主控制器、页面状态、视频布局、按键组、启动 Logo |
sdk/lib/gui/81x_demo_ui/81x_demo_ui.h |
页面枚举、视频模式枚举、对外切换 API |
generated/gui_guider.c |
GUI Guider 生成的页面初始化、屏幕加载和动画辅助函数 |
generated/setup_scr_screen.c |
创建 320×240 透明主屏幕 |
generated/events_init.c |
GUI Guider 事件初始化入口,当前无业务事件 |
custom/custom.c |
用户自定义初始化入口,当前为空实现 |
ui_res/ui_bgLogo.c |
320×240 JPEG 启动 Logo 数据 |
3.3 关联模块
| 文件 | 与本功能的关系 |
|---|---|
project/main.c |
初始化摄像头、JPEG、LCD/LVGL,并在网络就绪后调用 user_protocol() |
sdk/app/video_app/video_app.c |
将 JPEG 编码结果绑定到 R_RTP_JPEG |
sdk/app/other_jpg_show/other_jpg_show_stream.c |
接收远端 JPEG,配置解码输出尺寸 |
sdk/app/app_lcd/app_lcd.c |
控制 P0/P1 使能、坐标和上下层关系 |
sdk/app/interface_management/interface_mgnt.c |
初始化 LVGL 并调用 ui_81x_demo_display() |
sdk/app/intercom/intercom_adpcm.c |
音频对讲复用已学习到的对端 IP |
4. 系统启动调用链
4.1 硬件与视频源初始化
project/main.c::hardware_init() 完成流框架、按键、JPEG、摄像头和 LCD/LVGL 初始化。
当 BBM_DEMO == 1 时,JPEG0 使用 VPP_DATA1:
1 | jpg_cfg(HG_JPG0_DEVID, VPP_DATA1); |
对应的编码尺寸也必须读取 photo_msg.out1_w/out1_h。工程已经在以下两处做了配套处理:
video_app.c中使用get_jpg_default_dpi(1)。jpg_v2.c::jpg_start()中使用photo_msg.out1_h/out1_w。
该约束非常重要。如果数据源使用 VPP_DATA1,却按 VPP_DATA0 的 640×480 尺寸读取一个实际为 320×240 的缓冲区,会出现 2×2 重复画面和绿色色度块。
4.2 UI 初始化
UI 启动链如下:
1 | hardware_init() |
主屏幕在 setup_scr_screen.c 中创建,尺寸为 320×240,背景透明。ui_81x_demo_apply_osd_key_bg() 又将显示器和屏幕底色设置为黑色,用于视频通道暂时无帧或关闭时的背景填充。
4.3 网络协议初始化
网络服务初始化完成后,project/main.c::app_network_init() 调用 user_protocol()。本模块提供强符号实现:
1 | user_protocol() |
protocol_client_init() 和 protocol_server_init() 最终也调用同一个 bbm_protocol_start()。bbm_protocol_started 保证初始化幂等,因此 AP 和 STA 两种角色共用同一套传输代码。
5. 本地视频发送链路
5.1 JPEG 数据来源
video_app.c 创建 JPEG 视频流后,将编码结果绑定到多个目的流,其中包括:
1 | streamSrc_bind_streamDest(s, R_RTP_JPEG); |
发送任务通过以下调用订阅该流:
1 | open_stream_available(R_RTP_JPEG, 0, 8, bbm_tx_stream_op, NULL); |
这里的 8 是目的端缓存节点数量。任务循环调用 recv_real_data() 获取编码帧,处理完成后必须调用 free_data(frame) 归还流框架。
5.2 兼容两种 JPEG 存储形式
bbm_copy_encoded_jpeg() 同时支持:
JPEG_FULL:完整 JPEG 位于连续内存,直接复制。- 分段 JPEG:数据由
stream_jpeg_data_s链表保存,逐节点复制到连续发送缓冲区。
复制完成后,bbm_find_jpeg() 搜索 JPEG SOI FF D8 和 EOI FF D9,只发送有效 JPEG 区间,避免把编码缓冲区中的前后填充数据发到网络。
5.3 分片发送
单帧最大值和 UDP 负载定义如下:
1 |
每帧最多 24 个分片。bbm_send_frame() 逐片调用 bbm_send_packet(),每包最多重试 3 次,重试和相邻分片之间均有 1 ms 让步。
发送主调用链:
1 | bbm_video_tx_thread() |
发送条件包括:
bbm_tx_enable == 1;- STA 模式下 Wi-Fi 已连接;
- 能够取得有效对端 IP。
6. UDP 私有协议
协议版本为 2,端口为 UDP 11011,协议头固定 28 字节。多字节字段统一使用网络字节序。
| 偏移 | 字段 | 类型 | 说明 |
|---|---|---|---|
| 0 | magic |
uint32_t |
固定值 0x42424D32,ASCII 近似为 BBM2 |
| 4 | frame_id |
uint32_t |
帧序号,发送端逐帧递增,允许 32 位回绕 |
| 8 | frame_len |
uint32_t |
完整 JPEG 长度,最大 30 KiB |
| 12 | frame_time |
uint32_t |
发送端 os_jiffies() 时间戳 |
| 16 | version |
uint16_t |
当前版本 2 |
| 18 | header_size |
uint16_t |
当前固定为 28 |
| 20 | fragment_index |
uint16_t |
当前分片编号,从 0 开始 |
| 22 | fragment_count |
uint16_t |
本帧分片总数,最大 24 |
| 24 | payload_len |
uint16_t |
当前 UDP 负载长度,最大 1300 |
| 26 | flags |
uint16_t |
bit0 为首片,bit1 为尾片 |
协议启动时会检查 sizeof(struct bbm_wire_header) == 28,用于发现编译器对齐策略变化。但结构体没有显式 packed 属性,因此移植到不同 ABI 或编译器时,仍应优先改为字段级序列化,而不是直接依赖结构体布局。
7. 远端接收与帧重组
7.1 接收任务
bbm_video_rx_thread() 在同一个 UDP socket 上调用 recvfrom(),接收超时为 20 ms。收到任意有效长度的数据包后,首先通过源地址学习对端 IP,再根据 bbm_rx_enable 决定是否解析视频包。
接收调用链:
1 | bbm_video_rx_thread() |
7.2 数据包校验
bbm_parse_packet() 校验:
- 包长度至少为 28 字节;
magic和version正确;header_size等于本地协议头大小;payload_len位于 1~1300;- UDP 实际长度等于协议头长度加负载长度。
bbm_rx_accept_packet() 进一步校验:
- 分片索引小于分片总数;
- 分片总数不超过 24;
offset未越过帧长度;- 每片长度与它在整帧中的位置严格匹配;
- 写入范围不超过 30 KiB 接收缓冲区。
7.3 乱序、重复和新旧帧处理
bbm_rx_assembler 保存当前正在组装的帧:
1 | struct bbm_rx_assembler { |
实现使用 32 位位图记录分片,能够接收乱序包并忽略重复包。新帧到达时:
- 若帧号比当前帧旧,记为
old_packet并忽略; - 若帧号更新,丢弃未完成旧帧并开始组装新帧;
- 帧号比较使用有符号差值,可正确处理
uint32_t回绕附近的常见情况。
一帧超过 150 ms 未收齐时,接收任务复位组帧状态并增加 frame_timeout。
7.4 投递到解码链路
完整 JPEG 通过固定槽位源流进入解码系统:
1 | S_BBM_REMOTE_JPG_SRC |
初始化顺序位于 bbm_video_stream_init():
- 创建 JPEG 解码流,绑定到 LCD P1。
- 创建远端
other_jpg流,绑定到 JPEG 解码器。 - 创建 4 槽位远端 JPEG 源流。
每个源槽位按需申请 30 KiB PSRAM,并在槽位归还时保留内存复用,只在 STREAM_DATA_DESTORY 时释放。因此稳定运行后的源槽位池最多占用约 120 KiB PSRAM。
bbm_submit_remote_jpeg() 的所有权规则是:
get_src_data_f()取得一个空闲data_structure。- 将 JPEG 复制到该槽位自己的 PSRAM 缓冲区。
- 设置
JPEG_FULL、真实长度和帧时间。 send_data_to_stream()成功后,所有权交给流框架。- 发送失败时调用
force_del_data()回收槽位。
8. LVGL 页面与视频布局
UI 定义四个可循环切换的页面:
| 页面 | 页面文字 | P0 本地 | P1 远端 | 顶层通道 | 远端解码 |
|---|---|---|---|---|---|
UI_81X_DEMO_PAGE_LOCAL |
LOCAL |
320×240,坐标 (0,0) | 关闭 | P0 | 关闭 |
UI_81X_DEMO_PAGE_REMOTE |
REMOTE |
关闭 | 320×240,坐标 (0,0) | P1 | 开启 |
UI_81X_DEMO_PAGE_LOCAL_BIG_REMOTE_SMALL |
LOCAL BIG |
320×240,坐标 (0,0) | 120×90,右上角 | P1 | 开启 |
UI_81X_DEMO_PAGE_REMOTE_BIG_LOCAL_SMALL |
REMOTE BIG |
120×90,右上角 | 320×240,坐标 (0,0) | P0 | 开启 |
右上角小窗横坐标计算公式:
1 | SCALE_WIDTH - 120 - 6 |
在 320×240 屏幕上即为 (194, 6)。
8.1 页面切换调用链
1 | LVGL 按键事件 |
8.2 本地预览流
本地画面不是直接使用编码 JPEG,而是创建缩放流:
1 | scale3_stream_not_bind( |
随后绑定到 R_VIDEO_P0。每次改变本地显示尺寸时,代码会关闭旧流并按新尺寸重新创建,确保全屏和小窗模式使用正确的缩放参数。
8.3 远端显示尺寸
UI 通过以下接口设置远端 JPEG 解码输出尺寸:
1 | bbm_protocol_set_remote_display_size(width, height); |
该函数把宽高编码到一个 32 位参数中:
1 | ((uint32_t)width << 16) | height |
然后通过 SET_LVGL_VIDEO_ARG 发给 other_jpg 流。other_jpg_cmd_func() 在关中断区间更新 out_w/out_h/step_w/step_h,避免解码工作与页面切换同时读写尺寸参数。
8.4 LCD 布局更新
lcd_stream_set_video_enable() 和 lcd_stream_set_video_layout() 都在关中断区间更新 app_lcd_s 状态:
video_p0_enable、video_p1_enable:控制两路视频是否输出;p0_x/y、p1_x/y:控制两路画面坐标;p0_up:控制 P0/P1 的叠放顺序;layout_pending:通知 LCD 线程应用新布局;clear_p0_pending/clear_p1_pending:关闭通道时请求清理旧画面。
9. 对端 IP 学习与音视频联动
默认对端地址由 Wi-Fi 角色决定:
| 本机模式 | 默认对端 IP |
|---|---|
| STA | 192.168.1.1 |
| AP | 192.168.1.100 |
接收任务收到 UDP 包后,以 recvfrom() 返回的源 IP 更新 bbm_peer_ip,同时设置 bbm_peer_ready。音频对讲模块 intercom_adpcm.c 也调用 bbm_protocol_get_peer_ip(),因此视频链路学到的 IP 可以被音频链路复用;反过来,音频 ACK 或 UDP 包也会调用 bbm_protocol_set_peer_ip() 更新同一个对端地址。
连续 2000 ms 未收到视频包时,接收任务清除:
bbm_peer_ready;bbm_remote_frame_ready;- 动态学习到的
bbm_peer_ip。
之后发送端会重新退回 AP/STA 对应的默认地址。
10. 任务、并发与内存
10.1 任务配置
| 任务 | 入口 | 优先级 | 栈大小 | 主要阻塞点 |
|---|---|---|---|---|
video2_rx |
bbm_video_rx_thread() |
15 | 2048 | recvfrom(),20 ms 超时 |
video2_tx |
bbm_video_tx_thread() |
15 | 2048 | 轮询视频流,失败时短暂 sleep |
gui_thread |
lvgl_run() |
NORMAL | 4096 | LVGL 主循环 |
lcd_thread |
lcd_thread() |
NORMAL | 1024 | LCD 输出与布局更新 |
视频任务均为永久循环,目前没有停止和销毁路径。
10.2 主要缓冲区
| 缓冲区 | 大小 | 存储区域 | 用途 |
|---|---|---|---|
bbm_tx_packet |
1328 B | 静态内存,4 字节对齐 | UDP 发送包 |
bbm_rx_packet |
1328 B | 静态内存,4 字节对齐 | UDP 接收包 |
bbm_tx_jpeg |
30 KiB | .psram.src |
连续化后的本地 JPEG |
bbm_rx_jpeg |
30 KiB | .psram.src |
远端帧重组 |
| 远端源槽位池 | 最多 4 × 30 KiB | 动态 PSRAM | 向解码流投递完整 JPEG |
仅视频对讲模块稳定运行时,JPEG 大缓冲区的 PSRAM 上限约为 180 KiB,未包含流框架节点、JPEG 解码器和 LCD 帧缓冲。
10.3 并发模型
- TX 任务独占
bbm_tx_packet和bbm_tx_jpeg。 - RX 任务独占
bbm_rx_packet、bbm_rx_jpeg和bbm_rx_frame。 - UI/LVGL 任务写
bbm_tx_enable、bbm_rx_enable、bbm_displaydecode_run。 - 音频、RX 和 TX 任务可能同时访问对端状态变量。
- 这些简单状态使用
volatile,但没有互斥锁或原子操作。当前目标平台对自然对齐的 8/32 位访问通常是单次操作,但volatile不提供跨核同步、复合操作原子性或内存屏障保证。
11. 对外 API
11.1 视频协议控制 API
| API | 作用 |
|---|---|
bbm_protocol_set_tx_enable(enable) |
开关本机 JPEG 网络发送 |
bbm_protocol_get_tx_enable() |
查询发送状态 |
bbm_protocol_set_rx_enable(enable) |
开关收到包后的协议解析 |
bbm_protocol_get_rx_enable() |
查询接收状态 |
bbm_protocol_set_remote_display_enable(enable) |
开关完整远端帧向解码流投递 |
bbm_protocol_get_remote_display_enable() |
查询远端显示门控 |
bbm_protocol_set_remote_display_size(w, h) |
设置远端解码输出尺寸 |
bbm_protocol_set_peer_ip(ip) |
设置/学习对端 IPv4 地址,参数使用网络字节序 |
bbm_protocol_get_peer_ip() |
获取动态地址;未学习时返回默认地址 |
bbm_protocol_is_peer_ready() |
是否已收到对端数据并进入 ready 状态 |
bbm_protocol_has_remote_frame() |
是否至少成功投递过一帧远端 JPEG |
bbm_protocol_clear_peer_ready() |
清除对端和远端首帧状态 |
11.2 UI 控制 API
| API | 作用 |
|---|---|
ui_81x_demo_display() |
显示启动 Logo,并在 3 秒后进入主界面 |
ui_81x_demo_set_video_mode(mode) |
直接设置视频显示模式 |
ui_81x_demo_switch_page(page) |
切换到指定页面 |
ui_81x_demo_next_page() |
循环切换到下一页 |
ui_81x_demo_prev_page() |
循环切换到上一页 |
ui_81x_demo_show_remote_full() |
只显示远端全屏 |
ui_81x_demo_show_local_full() |
只显示本地全屏 |
ui_81x_demo_show_local_big_remote_small() |
本地全屏、远端右上小窗 |
ui_81x_demo_show_remote_big_local_small() |
远端全屏、本地右上小窗 |
ui_81x_demo_hide_video() |
关闭 P0/P1 视频显示 |
12. 调试日志说明
发送端每秒打印一次:
1 | video2_tx peer:... frame:... drop:... pkt:... fail:... empty:... speed:...KB/s fd:... |
| 字段 | 含义 |
|---|---|
frame |
成功发送的完整 JPEG 帧数 |
drop |
JPEG 提取失败或任一分片发送失败的帧数 |
pkt |
成功发送的 UDP 分片数 |
fail |
经过 3 次重试仍失败的分片数 |
empty |
本次轮询未取得 JPEG 帧的次数 |
speed |
本秒发送协议数据量,单位 KiB/s |
接收端每秒打印一次:
1 | video2_rx en:... pkt:... invalid:... old:... dup:... |
重点字段:
| 字段 | 含义及排查方向 |
|---|---|
invalid |
协议头、长度或分片参数非法;检查版本、端口和数据损坏 |
old |
收到比当前组装帧更旧的分片;通常由网络乱序或延迟引起 |
dup |
重复 UDP 分片 |
drop |
新帧到达时旧帧仍未完成 |
timeout |
150 ms 内未收齐一帧 |
gated |
已收齐,但当前 UI 不需要显示远端画面 |
stream |
成功投递到远端 JPEG 解码流的帧数 |
busy |
4 个远端源槽位全部占用,解码链路消费不及时 |
mem |
源槽位 PSRAM 申请失败 |
route |
send_data_to_stream() 失败 |
got x/y |
当前帧已收到分片数/总分片数 |
13. 常见问题定位
13.1 本地画面正常,远端黑屏
按以下顺序检查:
video2_rx pkt是否增长。invalid是否持续增长。frame_complete是否增长。- 当前页面的
en是否为 1;本地全屏页会主动关闭远端投递。 stream是否增长,busy/mem/route是否异常。lcd_stream_set_video_enable()是否已开启 P1。SET_LVGL_VIDEO_ARG的输出尺寸是否与当前页面一致。
13.2 远端画面卡顿或马赛克
timeout/drop高:Wi-Fi 丢包、单帧分片过多或 150 ms 超时太短。fail高:发送 socket 繁忙,当前非阻塞发送重试仍不足。busy高:JPEG 解码或 LCD 消费速度低于网络输入速度。- JPEG 超过 30 KiB:发送端会整帧丢弃,可降低分辨率/质量或提高上限。
当前协议没有校验和、FEC、NACK 或关键帧重传。UDP 包丢失会导致整帧在超时或新帧到达时被丢弃,但不会污染下一帧。
13.3 画面出现 2×2 重复或绿色块
优先检查 JPEG0 的源和尺寸是否匹配:
jpg_cfg(HG_JPG0_DEVID, VPP_DATA1);get_jpg_default_dpi(1);jpg_start()使用photo_msg.out1_w/out1_h。
这三处必须保持一致。
13.4 页面切换后残留旧画面
检查 lcd_stream_set_video_enable() 是否触发 clear_p0_pending 或 clear_p1_pending,以及 LCD 线程是否消费这些标志。布局函数只更新坐标与层级,不负责关闭旧通道。
14. 二次开发指南
14.1 新增一种布局
- 在
ui_81x_demo_video_mode_t中增加模式。 - 如需成为可翻页页面,在
ui_81x_demo_page_t中增加页面。 - 在
ui_81x_demo_page_name()中增加 OSD 名称。 - 编写布局函数,设置 P0/P1 使能、远端尺寸、坐标和本地缩放尺寸。
- 在
ui_81x_demo_set_video_mode()中增加分支。 - 在
ui_81x_demo_apply_page()中建立页面到视频模式的映射。 - 验证 PIP 坐标没有超出
SCALE_WIDTH/SCALE_HIGH。
14.2 修改分辨率
需要同步检查:
- 摄像头输入
photo_msg.in_w/in_h; - VPP DATA0/DATA1 输出尺寸;
- JPEG0 编码源和默认 DPI;
BBM_JPEG_MAX_SIZE是否容纳新 JPEG;- LCD P0 本地缩放输出;
- P1 远端解码输出;
- UI 主屏幕和 PIP 坐标;
- PSRAM 总占用。
14.3 修改 UDP 分片大小
双方必须同时修改 BBM_FRAGMENT_PAYLOAD。还要同步评估:
bbm_tx_packet、bbm_rx_packet大小;BBM_MAX_FRAGMENTS;received_map位数;- Wi-Fi MTU,避免 IP 层再次分片;
- 发送间隔和帧超时。
当前 1300 字节负载加 28 字节私有头、8 字节 UDP 头和 20 字节 IPv4 头,总计约 1356 字节,低于常见 1500 字节 MTU。
14.4 增加协议可靠性
推荐按兼容性从低到高逐步扩展:
- 在协议头尾部增加 CRC32,并升级
BBM_WIRE_VERSION。 - 增加会话 ID,避免设备重启后旧包与新帧号混淆。
- 增加接收端丢片统计和周期性反馈。
- 对关键帧或低分片数帧增加选择性重传。
- 网络较差时动态降低 JPEG 质量或帧率。
15. 已知风险与改进建议
以下项目不会阻止当前功能运行,但在产品化前值得处理:
- 任务创建返回值未检查。
csi_kernel_task_new()失败后仍会把协议标记为已启动,且没有回滚 socket 和流资源。 - 部分初始化失败没有统一清理。 解码流成功而后续源流失败时,已创建资源保持打开。
- 没有停止/重启接口。 两个任务永久循环,socket、流和 PSRAM 槽位仅适合一次性启动模型。
- 协议结构依赖 ABI。 虽然检查了 28 字节大小,但更稳妥的做法是显式序列化各字段。
- 共享状态只使用
volatile。 多任务并发更新对端状态时缺少明确同步机制。 - 对端学习接受任意 UDP 源。 在解析协议头之前就更新 peer IP,局域网内发往 11011 端口的任意数据都可能改变发送目标。建议至少在 magic/version 校验后再学习,产品环境可增加配对标识或鉴权。
- 未校验 FIRST/LAST 标志。 当前主要依赖 index/count,标志字段被发送但接收端没有验证。
- 无数据完整性校验。 UDP 校验和之外没有帧级 CRC,错误载荷可能进入 JPEG 解码器。
- UI 重入清理不完整。
ui_81x_demo_display()若被多次调用,旧定时器、按键组和页面对象的生命周期需要额外确认。 - 公共声明存在重复。
video_intercom.h与sdk/include/bbm_protocol.h维护同一组 API,后续容易发生声明不一致,建议保留单一公共头文件。
16. 构建与集成检查清单
工程的 project/fpv_app_umac4.mk 已包含:
sdk/app/video_intercom/video_intercom.c;sdk/lib/gui/81x_demo_ui/81x_demo_ui.c;generated/下的 GUI Guider 源文件;custom/custom.c;ui_res/ui_bgLogo.c。
编译前确认:
BBM_DEMO == 1;JPG_EN == 1;DVP_EN == 1;LCD_EN == 1;LVGL_STREAM_ENABLE与目标 LVGL 显示路径一致;KEY_MODULE_EN == 1时按键输入已注册;- 工程中只保留一个强符号
user_protocol()实现,避免与旧版babyprotocol.c同时链接产生冲突或选错协议实现; - 链接脚本存在
.psram.src段,并且 PSRAM 容量足够。
17. 核心流程总结
这套视频对讲方案的关键设计可以概括为三层:
- 媒体层:JPEG0 从
VPP_DATA1编码,本地 YUV 通过 P0 预览,远端 JPEG 通过 P1 解码显示。 - 传输层:单帧 JPEG 按 1300 字节切分,通过 UDP 11011 双向传输;接收端支持乱序、重复包过滤、新旧帧判断和 150 ms 超时丢帧。
- 交互层:LVGL 提供启动 Logo、页面名称和按键事件,页面状态机统一控制本地/远端通道使能、尺寸、位置和层级。
开发和调试时应始终沿着以下顺序定位问题:
1 | 摄像头/VPP → JPEG 编码 → R_RTP_JPEG → UDP 发送 |
只要明确每一层的数据格式、缓冲区所有权和开关状态,就能快速区分问题究竟发生在编码、网络、解码、LCD 合成还是 UI 控制环节。