前言

随着两客一危(旅游客运、包车客运、三类以上班线客运和危险货物运输)安全监管力度的不断加大,单纯的位置汇报(JT/T 808 协议)已经无法满足行业监管和企业主动安全防范(如 ADAS 驾驶辅助、DSM 疲劳驾驶监测)的需求。如何在定位车辆的同时,实时调取车载摄像头画面、进行历史视频回放以及下发语音对讲?

为此,交通运输部推出了 JT/T 1078 协议(全称《道路运输车辆卫星定位系统视频通信协议》)。它是 JT/T 808 协议的音视频扩展版。

对于车联网开发者来说,JT/T 1078 引入了全新的流媒体处理架构,比 808 协议更为复杂。本文将为你全面剖析 JT/T 1078 的双链架构、控制信令、1078 特化的 RTP 报文结构,以及如何从零设计一个高性能的 1078 流媒体分发网关。


一、JT/T 1078 的“信令+媒体”双链架构

JT/T 1078 并不能脱离 JT/T 808 独立运行。它的核心思想是:“信令走 808 链路,媒体流走独立 1078 链路”。

  1. 信令控制面(利用 808 长连接):
    • 平台下发“点播视频”、“云台控制”、“历史视频检索”等指令时,这些指令被封装为特殊的 808 消息(如 0x9101 实时音视频请求),通过已有的 JT/T 808 TCP 长连接发送给车载终端。
  2. 媒体传输面(新建 1078 独立流通道):
    • 车载终端收到点播指令后,会根据指令中携带的 流媒体服务器 IP 和端口,主动建立一条全新的 TCP 或 UDP 通道,将实时采集到的摄像头音视频流打包成 1078 RTP 数据包推送给流媒体服务器(网关)。
    • 网关收到流后,进行协议解包与转封装,分发给 Web/App 前端播放。

二、核心控制信令解析

信令交互是 1078 视频点播的前提。以下是平台发起“实时视频索要”的经典时序流程:

1. 0x9101 实时音视频传输请求(平台下发)

这是最关键的指令,用来命令终端开始推流。它的消息体属性包含:

  • 服务器IP地址(终端要往哪里推流,支持 IP 字符串或域名)。
  • 服务器TCP端口 / UDP端口。
  • 逻辑通道号:对应车辆的第几路摄像头(如 1:驾驶员,2:前路路况)。
  • 数据类型:0:音视频,1:视频,2:双向对讲,3:监听,4:广播。
  • 码流类型:0:主码流(高清,用于存储和重点监控),1:子码流(流畅,省流量)。

2. 0x9102 音视频流控制(平台下发)

用于在播放中控制媒体流,比如:

  • 控制指令:0:关闭当前通道的流,1:切换为当前通道的子/主码流,2:丢弃当前的 I 帧(快速降低延迟),3:关声音,4:开声音。

三、1078 特化的 RTP 报文结构

一旦建立了流媒体通道,车载终端就会以秒级数十包的频率推送音视频流。为了标识这些媒体帧,JT/T 1078 并没有直接采用标准的 RFC 3550 RTP 协议,而是定义了一个专有的 1078 头(1078 RTP Payload Header)。

1. 数据包整体结构

每一个发送给网关的数据包都是由 1078 专属头 + 音视频负载 组成:

帧头标识 (4B) V/P/X/CC (1B) M/PT (1B) 包序号 (2B) 终端手机号 (6B) 逻辑通道号 (1B) 数据类型 (1B) 分包标记 (1B) 相对时间戳 (8B) 上一I帧间隔 (2B) 上一帧间隔 (2B) 负载长度 (2B) 媒体负载 (长度不定)
0x30 0x31 0x63 0x64 0x80 等 Payload Type Sequence BCD 手机号 Channel Data Type Sub-Flag Timestamp Last I-Frame Interval Last Frame Interval Length e.g. H.264 NALU

2. 核心字段深度解析

  • 帧头标识:固定 4 字节,为字符 "01cd" 的 ASCII 码:0x30 0x31 0x63 0x64。这用来在 TCP 流中作为包头边界进行切包判定。
  • V/P/X/CC (1字节):固定为 0x80 或 0x81,与标准 RTP 头部字段兼容。
  • M/PT (1字节):
    • M (bit 7):标志位,为 1 表示一帧视频的结束(最后一个分包)。
    • PT (bit 0-6):负载类型(Payload Type)。常见的有 98: H.264,99: H.265,8: G.711A,26: G.726,19: AAC 等。
  • 数据类型 (1字节):
    • 0x00:视频 I 帧(关键帧,包含完整的画面信息,必须由此帧开始渲染)。
    • 0x01:视频 P 帧(预测帧,只包含与前一帧的差异)。
    • 0x02:视频 B 帧(双向预测帧)。
    • 0x03:音频数据。
    • 0x04:透传数据。
  • 分包标记 (1字节):
    • 用来标记一帧大视频被拆分成多个网络包的情况。
    • 0000:第一包;0001:中间包;0010:最后一包;0011:独立包(不需要分包)。
  • 时间戳 (8字节):使用毫秒(ms)级的相对时间戳,用于音视频同步(AV Sync)和播放器缓存控制。

四、如何设计高性能 1078 流媒体分发网关?

1078 媒体网关是整个车载视频系统中最吃 CPU 和网络带宽的组件。一个优秀的 1078 流媒体服务器,其处理流程通常如下:

核心开发步骤

第一步:基于 Netty 监听并收流

因为 1078 媒体流是 TCP 二进制流,且包含边界魔数 "01cd"。我们使用自定义的 Netty 帧解码器(根据魔数定位和包头中 负载长度 字段动态切包):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class JT1078FrameDecoder extends LengthFieldBasedFrameDecoder {

public JT1078FrameDecoder() {
// 参数说明:
// maxFrameLength: 最大帧长度(如 65535)
// lengthFieldOffset: 长度字段偏离包头的字节数。
// 从 1078 头字节排布看,"负载长度" 字段在第 28 和 29 字节(索引28,长度2)
// lengthFieldLength: 长度字段本身的长度 (2字节)
// lengthAdjustment: 长度字段后的剩余数据长度调整,由于1078包头总长 30 字节,长度字段在 28-29,所以包头还剩 0 字节,调整为 0
// initialBytesToStrip: 剥离前面的字节,我们选择不剥离(设置为 0),保留完整 1078 头部用于解析元数据
super(65535, 28, 2, 0, 0);
}

@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
// 先利用魔数 "01cd" 进行对齐定位,防止因为网络抖动错位
if (in.readableBytes() < 4) return null;

in.markReaderIndex();
byte[] magic = new byte[4];
in.readBytes(magic);
if (magic[0] != 0x30 || magic[1] != 0x31 || magic[2] != 0x63 || magic[3] != 0x64) {
in.resetReaderIndex();
in.readByte(); // 错位了,丢弃 1 字节并继续寻找魔数
return null;
}

in.resetReaderIndex();
return super.decode(ctx, in); // 调用父类 LengthField 进行精确切包
}
}

第二步:提取 NALU 并进行帧重组

对于 H.264/H.265 编码,如果遇到“分包标记”为“第一包”或“中间包”,网关需要先开辟缓冲区,把后面的数据拼接在一起,直到收到“最后一包”或“独立包”,才把这一帧拼好的 NALU(需在帧前加上 0x00 0x00 0x00 0x01 起始码)送往推流器。

第三步:集成 ZLMediaKit / SRS 流媒体引擎实现转协议播放

自己写推流和分发协议(如 WebSockets-FLV、WebRTC)代价极大。最佳的工业实践是集成成熟的开源流媒体服务器。

  • 方案:1078 网关解包还原出 H.264 裸流和音频帧后,通过本地 RTMP 推流(使用 Java-CV、FFmpeg 或 ZLMediaKit API)将流推送到流媒体引擎。
  • 分发:流媒体引擎自动生成 HTTP-FLV、HLS 和 WebRTC 等多种流,网页前端使用 flv.js 或标准的 WebRTC 播放器,延迟可控制在 1 秒以内!

五、1078 关键避坑与优化指南

  1. G.711 / G.726 音频转码:
    • 车载摄像头音频很多采用 G.711A、G.711U 或 G.726。这些音频格式直接丢给浏览器播放是无声的,因为主流浏览器(HTML5 audio)只支持 AAC 或 Opus。
    • 优化方案:在 1078 流媒体网关内部,通过 FFmpeg 库或 EasyAACEncoder 对音频帧进行重采样并实时转码为 AAC,再混流推给前端。
  2. 丢包导致的画面马赛克(针对 UDP 接入模式):
    • 如果终端以 UDP 协议上报视频,弱网下极易发生丢包。如果丢失了 I 帧或关键的 P 帧分包,会导致画面持续花屏。
    • 优化方案:网关检测到 Sequence 流水号不连续时,应立即向终端发送 0x9102 信令指令,要求终端“丢弃当前P帧,强制上报下一个I帧”以快速恢复画面清晰度。
  3. 假死连接断开:
    • 车载终端处于移动状态,在通过隧道或山区时连接可能会假死挂起。1078 网关必须配置 Idle 检测,在 10 秒内未收到媒体包时应主动释放连接,避免内存泄露。