通过 Wi-Fi 接收手机的 H.264 / MJPEG 视频,接入解码器和 LCD 显示。普通视频与独立镜像均可使用 FreeRTOS 托管入口;镜像需要额外启用独立控制连接。先完成首次添加与主业务连接;大屏导航还须同时接入导航 HUD。
声明视频能力
大屏完成 HUD 接入后,在创建 SDK 前配置实际可用的解码器。例如支持 MJPEG 的 1024×600 屏幕:
c.width = 1024;
c.height = 600;
c.mjpeg.enabled = true;
c.mjpeg.fps = 13;
c.mjpeg.bitrate = 12000000; // bit/s
这是参数示例,帧率要按开发板的实际显示能力填写。支持 H.264 时配置 c.h264。启用的视频格式必须填写 1–30 FPS 和正数码率;宽高为非零偶数,不超过 1920×1080。
按顺序准备热点和屏幕
e 是收到的原事件;异步完成时使用之前保存的那一份。
| 收到的事件 | 开发板要做什么 | 完成后调用 |
|---|---|---|
MOTO_EVENT_WIFI,purpose=0 |
复用已运行的主业务热点,核对 IP、DHCP 和 UDP 监听 | moto_terminal_wifi_ready(t, &e, &ap) |
MOTO_EVENT_STREAM_PREPARE |
按 e.data.stream 的尺寸和编码格式准备解码器、收包及显示缓冲 |
moto_terminal_complete(t, &e, MOTO_SUCCEEDED, 0) |
MOTO_EVENT_STREAM_START |
开始接收本次视频,保存 e.session 和 stream_id |
等屏幕真正显示一帧后再确认 |
MOTO_EVENT_STREAM_STOP |
停止收包、解码和显示提交 | 清理本次视频资源 |
STREAM_STOP 只结束视频流,不代表导航结束,不要据此清除仍有效的 HUD。收到 HUD_EXPIRED 时清除过期提示,收到 NAV_END 或 CLOSED 时清理导航界面。
热点在 30 秒内准备好,视频准备在 10 秒内完成;失败用 complete(..., MOTO_FAILED, reason) 回报。purpose=1 是固件升级请求,purpose=2 是广告传输请求;两者均不进入视频流程。
ap 是 moto_terminal_hotspot:填实际 SSID(1–32 字节)、密码(8–63 字节)、IP 和 UDP 端口。参考热点使用 192.168.4.1:5001;其他地址须与 APP 已认证的设备地址及实际监听端口一致,不能仅修改这个示例值。热点可连接后再回报,不在日志中输出密码。
就绪前逐项确认:AP 使用 WPA2 和设备配置的热点密码;AP 网卡地址与上报的 IP 一致;DHCP 可给手机分配同网段地址;UDP 监听器已绑定端口 5001。保存 AP 关联设备与 DHCP 租约信息,用于将后续视频来源关联到当前手机。不能在仅提交“启动热点”请求时就返回成功。wifi_ready 返回 MOTO_FULL 时保留原事件和热点参数,稍后重试;会话失效则释放本次视频资源,保留主业务 AP 与监听。
把视频包变成屏幕画面
使用 moto/video.h 的视频重组器,把多个 UDP 包拼成完整压缩帧,再交给你的解码器:
UDP 收包 → moto_video_receive → 完整帧回调
→ MJPEG / H.264 解码 → LCD 显示完成 → moto_terminal_presented
创建重组器后,按下节绑定会话并收包。完整帧内存仅在回调中有效,异步解码前复制到自己的缓冲区。H.264 缺关键帧时用 moto_terminal_keyframe 请求恢复。
保存视频会话与网络对端
session 是控制会话编号,stream_id 是视频流编号,peer_token 是板级网络适配器给已核验手机分配的非零标识。三个值分别保存,不能互相替代,也不能把 peer_token 固定填成 1。
- 通过当前 AP 的关联设备、DHCP 租约和本次连接上下文核验手机。仅接受该手机的源 IP,锁定并核对其视频源端口;不要把任意首个 UDP 包认作已认证手机。
- 收到安全通道的
MOTO_EVENT_STREAM_START后,将整个事件复制到视频任务。网络适配器为本次已核验对端分配peer_token。 - 视频任务调用
moto_video_begin_codec(video, peer_token, stream_id, width, height, codec)并检查返回值。只有MOTO_OK后才将数据交给重组器。 - 每个通过源地址检查的数据报调用
moto_video_receive(video, peer_token, bytes, size, now_ms);同一流始终传同一个 token。每 10–20 ms 调用moto_video_tick(video, now_ms)。UDP 包最多 1400 字节,保持包边界;moto_video_tick不接收 token。 - 解码任务保存帧所属的
session、stream_id、frame_id;显示完成且原流仍有效时调用moto_terminal_presented。MOTO_FULL时只重试仍有效的原流回执。 STREAM_STOP、NAV_END、CLOSED或网络断开时,在视频任务中调用moto_video_stop(video),撤销对端 token,取消旧解码和显示任务。不要在旧流停止后提交它的迟到回执。
调用顺序示意(verified_peer 由你的网络适配器生成,stream 为保存的 e.data.stream):
moto_result rc = moto_video_begin_codec(
video, verified_peer, stream.stream_id,
stream.width, stream.height, stream.codec);
// MOTO_OK 后,在同一视频任务中处理已核验的数据报:
rc = moto_video_receive(video, verified_peer, bytes, size, monotonic_ms);
moto_video_tick(video, monotonic_ms);
// 停止当前流:
moto_video_stop(video);
moto_video_* 对同一实例的调用必须串行;完整帧和关键帧回调不可重入该实例。重组器创建失败时不继续准备视频,关闭时在停止所有调用者后 moto_video_destroy(video)。
iOS 屏幕镜像
手机先加入设备热点并完成 Wi-Fi 主业务认证。用户在 APP 选择屏幕镜像,再在系统面板点击“开始广播”;只有视频准备与启动获得终端确认后,广播扩展才开始编码发送。仅显示“开始广播”不代表设备已有画面,最终以 LCD 呈现回执和实际显示验收。
屏幕镜像与导航投屏共用视频接收、解码和显示接口;镜像不启动导航 HUD。蓝牙不是镜像前提。
Managed 在 create 成功后、start 前调用 moto_terminal_set_mirror(terminal, true),检查返回值后才启动。要求已配置完整 config.network、真实视频解码能力,且主业务端口不能为 5005;高级外部主传输模式不支持此入口。由生命周期管理任务串行配置,stop/start 保留设置。SDK 任务管理 TCP 5005 的独立镜像 TLS、授权和清理,客户继续实现视频接收、解码和显示,不另建镜像 SDK 任务。
Core 工程仍可使用 moto_config.mirror_control=true、mirror_auxiliary 和 moto_connect_mirror,由同一个所有者线程串行驱动实例;PROFILE key32 声明镜像控制能力。Core 的主 TCP 意外丢失通过 moto_detach_mirror 保留已委派且有效的镜像,并持续 poll;Managed 自动管理这一流程。
广播扩展使用仅本流有效的授权,每 2 秒续期并接收呈现/关键帧回执。主 TCP 的 EOF/IO 可以保留有效镜像;整网断开、显式停止、授权撤销或过期则释放媒体。r26 且声明 PROFILE39 的终端允许配套 App SDK 通过全新 TLS 与当前 owner/镜像授权恢复主业务;恢复前保持控车关闭,恢复后重新读取车况。旧终端仍需停止广播后再连接。详见主业务恢复。
扩展 10 秒无应答停止发送,终端 20 秒无有效续期结束镜像。辅助连接关闭不应当作主业务 CLOSED 处理。视频仍通过 UDP 5001 明文传输。
当前 APP 首页使用一个投屏入口。未开通 Wi-Fi 音频时走纯视频,不申请声音权限,车辆管理隐藏音频连接区。已开通且运行条件满足时才附加声音;用户仍按系统流程授权。
Managed 音频还须在 start 前调用 moto_terminal_set_audio,接入真实 sink 和独立 TLS 5006 通道。能力门禁、采集来源和停止规则见音频接入。
