文章目录
- Q1:请介绍一下这个智能监控平台的整体架构和数据流?
- Q2:RockX 行人检测是怎么集成进推流链路的?检测框如何叠加到画面?
- Q3:为什么不是每帧都做行人检测?你会如何权衡检测频率?
- Q4:软硬件 H.264 编码分别怎么做?推流封装用的是什么?
- Q5:自定义 TCP 协议长什么样?注册、心跳、推流控制如何交互?
- Q6:Socket 超时、心跳超时、断线重连具体怎么做的?还有哪些可改进点?
- Q7:从 Web 点击「开始推流」到画面出来,完整调用链是什么?
- Q8:视频卡顿、设备离线、推流中断,你会怎么用 GDB / Wireshark 排查?
- Q9:如果推流稳定性出问题,你会从采集、编码、网络哪几侧优化?
- Q10:自定义协议相对 MQTT/gRPC 有何取舍?若让你重构设备通信,怎么设计?
项目要点:RockX 行人检测与画面叠加、软硬件 H.264 推流、自定义 TCP(注册/心跳/推流控制)、Socket/心跳超时与断线重连、GDB/Wireshark 故障排查。
项目主链路:Web → Server(HTTP+TCP) → Device → RTMP 推流 → 流媒体 → Web 拉流。
Q1:请介绍一下这个智能监控平台的整体架构和数据流?
这是一个四层监控系统:前端 React 负责设备管理和播放;监控服务器基于 Muduo,同时提供 HTTP API(约 8001)和设备 TCP 接入(约 8081);设备端负责注册、心跳、采集、AI 检测、编码和推流;流媒体服务器接收设备 RTMP/RTSP 推流,供 Web 通过 HLS/HTTP-FLV 拉流播放。用户点击开流后,Web 调 HTTP 接口,服务器通过长连接 TCP 下发推流命令和 URL,设备再推到rtmp://媒体IP/Streaming_Channels/{deviceId}_{channelId},实现远程监控闭环。
解析
| 层次 | 关键模块 | 职责 |
|---|---|---|
| Web | src/web | 设备列表、开/停流、播放 |
| Server | MonitoringServer+DeviceManager | HTTP 控流、TCP 管设备状态 |
| Device | Network+StreamManager+PushStreamer | 协议交互 + 采集编码推流 |
| Media | nginx-rtmp 等 | 收推流、对外拉流 |
串起来就是:
Web ──HTTP──► Server ──TCP──► Device ──RTMP──► 流媒体 ──拉流──► Web 播放「四层」= 前端 + 业务服务器 + 摄像头设备 + 流媒体
面试官常追问「为什么设备不用 HTTP 轮询」:因为开流是服务端主动下发,需要长连接保持deviceId → TcpConnection映射。
Q2:RockX 行人检测是怎么集成进推流链路的?检测框如何叠加到画面?
在 ARM/Rockchip 路径上,相机经 RKMedia VI 采集 NV12,再经 RGA 送入 AI 线程;使用rockx_create加载ROCKX_MODULE_PERSON_DETECTION_V2,调用rockx_person_detect做人检测。为降低时延,约每 5 帧检测一次,中间帧复用上一帧框;检测结果用 OpenCV 在 NV12 的 Y 平面上画矩形和文字,再把同一块 buffer 送给 VENC 做 H.264 硬件编码,因此叠加结果会直接进入推流码流,观众端无需再画框。
相机采 NV12 → RGA 给 AI → RockX 做人检测(约每 5 帧一次)→ OpenCV 在画面上画框 → VENC 硬编 H.264 推流。
解析
Camera/ISP → VI(NV12) → RGA ↓ RockX 检测(约 1/5 帧) ↓ OpenCV 画框(改 Y 平面) ↓ VENC H.264 → FFmpeg 封装推 RTMP- 关键实现:
push_streamer.cpp中rkmedia_vi_rockx_thread()。 - 为何改 Y 平面:NV12 亮度在 Y,灰度框/字在编码前写入,成本低且无需额外叠加图层。
- 独立 Demo(
rkmedia_rockx_person_detection.cpp)走 VO 本地显示,产品路径走 VENC+推流,面试时要分清。
Q3:为什么不是每帧都做行人检测?你会如何权衡检测频率?
单次 RockX 检测大约几十毫秒量级,若每帧都跑,容易拖垮整条实时链路,造成采集积压、编码/推流卡顿。项目里用detect_threshold = 5,隔帧检测并复用上一帧结果,在「框略滞后」和「实时流畅」之间折中。实际还可按分辨率、CPU/NPU 负载、目标运动速度动态调整:静止场景可更疏,高速移动可更密,或把检测放到独立线程并限制队列长度,避免阻塞编码线程。
解析
- 收益:降低 AI 算力占用,保证 VENC/推流节奏稳定。
- 代价:框有短暂延迟,快速穿越画面时可能「跟不上」。
- 可答优化:跟踪(Track)补帧、异步检测+最新结果覆盖、按 ROI 检测、降分辨率检测再映射回原图坐标。
Q4:软硬件 H.264 编码分别怎么做?推流封装用的是什么?
非 ARM/x64 走软件路径:V4L2 采集 → 格式转成 YUV420P → FFmpeglibx264编码,音频 ALSA → 重采样 → AAC,再用 FFmpeg 按 URL 选 FLV(RTMP)或 RTSP 封装写出。ARM 上视频走 Rockchip VENC 硬件 H.264(CBR、NV12 输入),编码后的MEDIA_BUFFER转成AVPacket交给同一套 FFmpeg 输出上下文推流;音频仍多用软件 AAC。也就是说:编码可软可硬,推流出口统一走 FFmpeg,方便一套逻辑兼容 RTMP/RTSP。
解析
| 平台 | 视频编码 | 音频 | 推流 |
|---|---|---|---|
| x64 | SwEncoder+libx264 | AAC | FFmpeg RTMP/FLV 或 RTSP |
| ARM | RKVENCH.264 | AAC(软件) | 同上 |
常见追问:为何硬编码还要 FFmpeg?——VENC 只出裸 H.264,RTMP 需要 FLV 复用、时间戳、网络写;FFmpeg 负责这一层。
Q5:自定义 TCP 协议长什么样?注册、心跳、推流控制如何交互?
协议是二进制头 + JSON 体:魔数0xAA、版本0x01、命令字、payload 长度、messageId、时间戳、16 字节 deviceId,后面跟 UTF-8 JSON。设备连上后发REGISTER_REQUEST(0x01),服务器回REGISTER_RESPONSE(0x02)并下发心跳间隔;之后设备周期性发HEARTBEAT_REQUEST(0x03)上报状态与各通道是否在推流;Web 开流时服务器发STREAM_CONTROL_REQUEST(0x07)(含start_stream/stop_stream和streamUrl),设备执行后回STREAM_CONTROL_RESPONSE(0x08)。这样控制面走 TCP,媒体面走 RTMP,职责分离。
解析
MessageHeader: magic | version | command | length | messageId | timestamp | deviceId[16] + JSON payload| 命令 | 方向 | 作用 |
|---|---|---|
| 0x01 / 0x02 | 设备 ↔ 服务器 | 注册 |
| 0x03 / 0x04 | 设备 ↔ 服务器 | 心跳 |
| 0x07 / 0x08 | 服务器 ↔ 设备 | 推流控制 |
流地址由服务器分配,设备不自己拼业务路径,便于统一管理和鉴权扩展。
Q6:Socket 超时、心跳超时、断线重连具体怎么做的?还有哪些可改进点?
设备连接时设置SO_RCVTIMEO/SO_SNDTIMEO约 5 秒,避免阻塞读写卡死;收包线程在非阻塞模式下遇EAGAIN短暂休眠继续等。心跳线程按间隔(注册后服务器常指定约 5 秒)发送;若发送失败、对端关闭(recv==0)或致命读错误,则走reconnect:断开 → 睡约 2 秒 → 重连 → 重新注册,失败再隔约 5 秒重试。配置里虽有heartbeatTimeout,当前更依赖「TCP 断开判定离线」;若要更严,应在服务器用定时器扫lastHeartbeatTime,超时主动踢下线并通知前端。
解析
- 已有机制:发送超时、读超时、断线重连、重注册。
- 薄弱点(可主动说,体现思考):
- 设备侧
heartbeatTimeout读取后未充分用于业务判定; - 服务器主要靠 TCP close 标离线,弱网「假在线」可能存在;
- 协议头未统一网络字节序、CRC 设计与实现不完全一致时,要讲清风险与兼容前提(同端序部署)。
- 设备侧
Q7:从 Web 点击「开始推流」到画面出来,完整调用链是什么?
前端请求服务器 HTTP 开流接口;MonitoringServer校验设备在线后,拼出媒体 URL,经 Muduo TCP 连接向设备发STREAM_CONTROL_REQUEST;设备Network解析命令,Device/StreamManager创建或启动对应通道的PushStreamer:采集 →(可选 RockX 叠加)→ 软/硬编码 → FFmpeg 推 RTMP;成功后回STREAM_CONTROL_RESPONSE,前端再用返回的播放地址从流媒体拉 HLS/HTTP-FLV。停流则反向发stop_stream,停采集编码并断开推流会话。
解析(步骤清单)
- Web → HTTP
start - Server:查
DeviceManager→ 组 URL → TCP 下发 - Device:
handleCommand→StartStream PushStreamer::Start:采集/AI/编码/推流线程起来- Media 收流就绪 → Web 播放器拉流
面试加分:说明控制面(TCP)与媒体面(RTMP)失败要分别处理,例如命令成功但推流失败需把错误带回响应或下次心跳的isStreaming。
Q8:视频卡顿、设备离线、推流中断,你会怎么用 GDB / Wireshark 排查?
先分层定位:卡顿优先看采集是否跟得上、编码耗时、发送阻塞;离线看 TCP 是否断、心跳是否停;推流中断看 RTMP 握手/写失败还是上游命令没到。GDB 挂设备进程,对卡顿线程看调用栈(是否堵在 RockX、av_write_frame、VENC GetBuffer);对崩溃看 core、空指针和缓冲生命周期。Wireshark 抓设备↔服务器端口看自定义协议帧是否粘包/半包、心跳是否规律;抓设备↔媒体 1935 看 RTMP chunk、是否重连、TCP 重传与窗口。两边时间线对齐,往往能区分「协议控制断了」还是「媒体链路断了」。
解析(可复述的排查表)
| 现象 | 优先怀疑 | 工具手段 |
|---|---|---|
| 画面卡、延迟升 | AI 过密、编码慢、网络上行不足 | GDB 栈、耗时日志、CPU |
| 设备显示离线 | TCP 断、重连失败、服务器踢连接 | Wireshark TCP、服务端连接表 |
| 有命令无画面 | URL 错、RTMP 鉴权/拒绝、编码器未出帧 | Wireshark RTMP、推流日志 |
| 间歇中断 | 弱网重传、心跳与业务争用、缓冲区满 | 抓包重传率 + 应用重连日志 |
Q9:如果推流稳定性出问题,你会从采集、编码、网络哪几侧优化?
采集侧保证缓冲队列有界、丢旧保新,避免积压导致延迟雪崩;AI 与编码解耦,检测失败不能阻塞送帧到 VENC。编码侧 ARM 优先硬编、合理 GOP/码率 CBR,关键帧间隔可调;软编则限制 preset/线程,防止 CPU 打满。网络侧对 RTMP 写失败快速失败并触发有限次重推;控制面 TCP 与媒体面分离,心跳只带轻量 JSON;必要时降分辨率/帧率做自适应。项目里已通过「隔帧检测 + 硬编 + 断线重连」覆盖常见稳定性点,线上再补指标(推流码率、丢帧、重连次数)闭环。
解析
- 采集:队列长度、背压策略。
- 编码:软硬切换、码率、IDR。
- 网络:超时、重连退避、与心跳线程协作避免同时狂重试。
- 可量化:端到端延迟、重连成功率、平均检测耗时——体现工程化思维。
Q10:自定义协议相对 MQTT/gRPC 有何取舍?若让你重构设备通信,怎么设计?
自定义 TCP 足够轻、与嵌入式设备契合,二进制头解析快,JSON 体便于扩展字段,Muduo 长连接也方便服务端主动推命令。代价是要自己处理粘包半包、心跳租约、重连、版本兼容和安全(加密鉴权)。若重构,我会保留「控制面长连接 + 媒体面 RTMP」分层;协议上明确大端序与长度字段、完整 CRC 或 length-prefix 帧、心跳超时双向强制离线;安全上 TLS 或至少 token 注册;实现上可用长度字段状态机解析,并加命令幂等(commandId)防止重连后重复开流。
解析(对比一句话版)
| 方案 | 优点 | 缺点 |
|---|---|---|
| 自定义 TCP(现状) | 可控、轻量、易嵌 | 工程细节多、易有帧/超时漏洞 |
| MQTT | 现成会话与 QoS | 需 Broker,嵌入式集成与运维成本 |
| gRPC | 强契约、流式 | 设备体积与依赖更重 |
结合本项目可主动提:当前协议设计里 CRC/字节序等细节与实现不完全对齐时,重构应优先「帧格式正确性 + 心跳租约」,再谈功能扩展。