部标JT1078协议RTP包解析

在 JT/T 1078 协议中视频的传输协议负载包定义如下:
image.png
用java开发的首选框架是netty,处理粘包、断包都很轻松,以下是解包的代码:

@Slf4j
public class Jt1078MediaFrameDecoder extends ByteToMessageDecoder {

    /**
     * RTP包固定字段长度
     */
    private static final int BASE_LEN = 18;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    private Object decode(ChannelHandlerContext ctx, ByteBuf buf) {
        if (buf.readableBytes() < BASE_LEN) {
            log.info("RTP包太小,{}", ByteBufUtil.hexDump(buf));
            return null;
        }

        //寻找包头0x30316364
        int headerIdx = -1;
        int readerIdx = buf.readerIndex();
        for (int i = readerIdx; i < buf.writerIndex() - 3; i++) {
            if (buf.getInt(i) == 0x30316364) {
                headerIdx = i;
                break;
            }
        }
        if (headerIdx == -1) {
            log.error("RTP包未找到包头0x30316364,{}", ByteBufUtil.hexDump(buf));
            buf.skipBytes(buf.readableBytes());
            return null;
        }

        //检测后续包长度
        buf.skipBytes(headerIdx - readerIdx);
        if (buf.readableBytes() < BASE_LEN) {
            log.info("RTP包后续长度太小,{}", ByteBufUtil.hexDump(buf));
            return null;
        }

        buf.markReaderIndex();
        int fullPacketLen = BASE_LEN;
        //解析出数据类型
        int dataType = buf.getByte(15 + headerIdx) >> 4;
        //时间戳,标识此RTP包当前帧的相对时间,单位毫秒,数据类型为0b0100时则没有该字段
        if (dataType != Constants.RTP_DATA_TYPE_0100) {
            if (buf.readableBytes() > fullPacketLen + 8) {
                fullPacketLen += 8;
            } else {
                log.info("RTP包时间戳后续长度太小,{}", ByteBufUtil.hexDump(buf));
                return null;
            }
        }

        //与上一个关键帧或上一帧之间的时间间隔,单位毫秒,数据类型为非视频帧时则没有该字段
        if (dataType != Constants.RTP_DATA_TYPE_0011 && dataType != Constants.RTP_DATA_TYPE_0100) {
            if (buf.readableBytes() > fullPacketLen + 4) {
                fullPacketLen += 4;
            } else {
                log.info("RTP包Last I Frame Interval和Last Frame Interval后续长度太小,{}", ByteBufUtil.hexDump(buf));
                return null;
            }
        }

        //数据体长度
        int bodyLen = buf.getShort(headerIdx + fullPacketLen - 2);
        //数据体长度太小则将读指针重置到包头,等待下一包后再解码
        if (buf.readableBytes() < fullPacketLen + bodyLen) {
            log.info("RTP包数据体长度小于{}", bodyLen);
            buf.resetReaderIndex();
            return null;
        }

        //解析出完整包,交给下一个处理器处理
        fullPacketLen += bodyLen;
        ByteBuf frame = buf.readRetainedSlice(fullPacketLen);
        return frame;
    }
}

官方网站:http://www.gps-pro.cn
开源地址:https://github.com/gnss-pro
微信:17158638841 或扫描下图
image.png

猜你喜欢

转载自blog.csdn.net/gps_pro/article/details/87516506