18045010223
8 天以前 afe371d39a054b2f2a9e5875b945584eec8a8141
src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Handler.java
@@ -24,18 +24,28 @@
    static Logger logger = LoggerFactory.getLogger(Jtt1078Handler.class);
    private static final AttributeKey<Session> SESSION_KEY = AttributeKey.valueOf("session-key");
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception
    {
    // @Override
    protected void channelRead011(ChannelHandlerContext ctx, Packet packet) throws Exception {
        io.netty.channel.Channel nettyChannel = ctx.channel();
        // 检查协议版本
        packet.seek(14);
        byte b14 = packet.nextByte();
        byte b15 = packet.nextByte();
        boolean is2019Protocol = (b14 & 0xFF) == 0x00 && (b15 & 0xFF) == 0x00;
        int simLength = is2019Protocol ? 10 : 6;
        // 读取SIM卡和通道号
        packet.seek(8);
        String sim = packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD() + packet.nextBCD();
        StringBuilder simBuilder = new StringBuilder();
        for(int i=0; i<simLength; i++) {
            simBuilder.append(packet.nextBCD());
        }
        String sim = simBuilder.toString();
        int channel = packet.nextByte() & 0xff;
        String tag = sim + "-" + channel;
        if (SessionManager.contains(nettyChannel, "tag") == false)
        {
        if (!SessionManager.contains(nettyChannel, "tag")) {
            Channel chl = PublishManager.getInstance().open(tag);
            SessionManager.set(nettyChannel, "tag", tag);
            logger.info("start publishing: {} -> {}-{}", Long.toHexString(chl.hashCode() & 0xffffffffL), sim, channel);
@@ -43,36 +53,131 @@
        Integer sequence = SessionManager.get(nettyChannel, "video-sequence");
        if (sequence == null) sequence = 0;
        // 1. 做好序号
        // 2. 音频需要转码后提供订阅
        int lengthOffset = 28;
        int dataType = (packet.seek(15).nextByte() >> 4) & 0x0f;
        int pkType = packet.seek(15).nextByte() & 0x0f;
        // 透传数据类型:0100,没有后面的时间以及Last I Frame Interval和Last Frame Interval字段
        if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2;
        else if (dataType == 0x03) lengthOffset = 28 - 4;
        // 动态获取数据类型和包类型
        int dataTypePos = is2019Protocol ? 19 : 15;
        packet.seek(dataTypePos);
        int dataType = (packet.nextByte() >> 4) & 0x0f;
        int pkType = packet.nextByte() & 0x0f;
        // 计算数据偏移量
        int baseOffset = is2019Protocol ? 32 : 28;
        int lengthOffset = baseOffset;
        if (dataType == 0x04) {
            lengthOffset = baseOffset - 8 - 2 - 2;
        } else if (dataType == 0x03) {
            lengthOffset = baseOffset - 4;
        }
        int pt = packet.seek(5).nextByte() & 0x7f;
        int timestampOffset = is2019Protocol ? 20 : 16;
        if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02)
        {
            // 碰到结束标记时,序号+1
            if (pkType == 0 || pkType == 2)
            {
        if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) {
            if (pkType == 0 || pkType == 2) {
                sequence += 1;
                SessionManager.set(nettyChannel, "video-sequence", sequence);
            }
            long timestamp = packet.seek(16).nextLong();
            PublishManager.getInstance().publishVideo(tag, sequence, timestamp, pt, packet.seek(lengthOffset + 2).nextBytes());
            long timestamp = packet.seek(timestampOffset).nextLong();
            byte[] videoData = packet.seek(lengthOffset + 2).nextBytes();
            logger.debug("Publishing video data - size: {}, seq: {}, ts: {}", videoData.length, sequence, timestamp);
            PublishManager.getInstance().publishVideo(tag, sequence, timestamp, pt, videoData);
        }
        else if (dataType == 0x03)
        {
            long timestamp = packet.seek(16).nextLong();
            byte[] data = packet.seek(lengthOffset + 2).nextBytes();
            PublishManager.getInstance().publishAudio(tag, sequence, timestamp, pt, data);
        else if (dataType == 0x03) {
            long timestamp = packet.seek(timestampOffset).nextLong();
            byte[] audioData = packet.seek(lengthOffset + 2).nextBytes();
            logger.debug("Publishing audio data - size: {}, seq: {}, ts: {}", audioData.length, sequence, timestamp);
            PublishManager.getInstance().publishAudio(tag, sequence, timestamp, pt, audioData);
        }
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Packet packet) throws Exception {
        io.netty.channel.Channel nettyChannel = ctx.channel();
        // 1. 协议版本检测
        boolean is2019Protocol = detectProtocolVersion(packet);
        int simLength = is2019Protocol ? 10 : 6;
        // 2. 读取SIM卡号和通道号
        packet.seek(8);
        StringBuilder simBuilder = new StringBuilder();
        for (int i = 0; i < simLength; i++) {
            simBuilder.append(packet.nextBCD());
        }
        String sim = simBuilder.toString();
        int channelPos = is2019Protocol ? 18 : 14;
        packet.seek(channelPos);
        int channel = packet.nextByte() & 0xff;
        String tag = sim + "-" + channel;
        // 3. 会话管理
        if (!SessionManager.contains(nettyChannel, "tag")) {
            Channel chl = PublishManager.getInstance().open(tag);
            SessionManager.set(nettyChannel, "tag", tag);
            logger.info("start publishing: {} -> {}-{} (Protocol: {})",
                    Long.toHexString(chl.hashCode() & 0xffffffffL),
                    sim, channel,
                    is2019Protocol ? "2019" : "2016");
        }
        // 4. 数据处理
        Integer sequence = SessionManager.get(nettyChannel, "video-sequence");
        if (sequence == null) sequence = 0;
        int dataTypePos = is2019Protocol ? 19 : 15;
        packet.seek(dataTypePos);
        int dataType = (packet.nextByte() >> 4) & 0x0f;
        int pkType = packet.nextByte() & 0x0f;
        int baseOffset = is2019Protocol ? 32 : 28;
        int lengthOffset = baseOffset;
        if (dataType == 0x04) lengthOffset = baseOffset - 8 - 2 - 2;
        else if (dataType == 0x03) lengthOffset = baseOffset - 4;
        int pt = packet.seek(5).nextByte() & 0x7f;
        int timestampOffset = is2019Protocol ? 20 : 16;
        if (dataType == 0x00 || dataType == 0x01 || dataType == 0x02) {
            if (pkType == 0 || pkType == 2) {
                sequence += 1;
                SessionManager.set(nettyChannel, "video-sequence", sequence);
            }
            long timestamp = packet.seek(timestampOffset).nextLong();
            byte[] videoData = packet.seek(lengthOffset + 2).nextBytes();
            PublishManager.getInstance().publishVideo(tag, sequence, timestamp, pt, videoData);
        }
        else if (dataType == 0x03) {
            long timestamp = packet.seek(timestampOffset).nextLong();
            byte[] audioData = packet.seek(lengthOffset + 2).nextBytes();
            PublishManager.getInstance().publishAudio(tag, sequence, timestamp, pt, audioData);
        }
    }
    private boolean detectProtocolVersion(Packet packet) {
        // 方法1:检查6字节SIM卡号后的填充
        packet.seek(8 + 6); // 2016协议SIM卡结束位置
        byte b14 = packet.nextByte();
        byte b15 = packet.nextByte();
        // 如果是2019协议,这里应该是0x00填充
        if ((b14 & 0xFF) == 0x00 && (b15 & 0xFF) == 0x00) {
            return true;
        }
        // 方法2:检查数据类型位置的有效性
        packet.seek(15);
        byte dtByte = packet.nextByte();
        int dataType = (dtByte >> 4) & 0x0F;
        int frameType = dtByte & 0x0F;
        // 如果数据类型或帧类型无效,可能是2019协议
        if (dataType > 4 || frameType > 3) {
            return true;
        }
        // 默认返回2016协议
        return false;
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception
    {