From afe371d39a054b2f2a9e5875b945584eec8a8141 Mon Sep 17 00:00:00 2001
From: 18045010223 <zjbassadmin@>
Date: 星期一, 28 七月 2025 09:09:05 +0800
Subject: [PATCH] 解决2019协议设备播放问题

---
 src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Decoder.java |  316 +++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 287 insertions(+), 29 deletions(-)

diff --git a/src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Decoder.java b/src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Decoder.java
index 9c2344c..4c07b05 100644
--- a/src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Decoder.java
+++ b/src/main/java/cn/org/hentai/jtt1078/server/Jtt1078Decoder.java
@@ -4,48 +4,306 @@
 import cn.org.hentai.jtt1078.util.ByteUtils;
 import cn.org.hentai.jtt1078.util.Packet;
 
-/**
- * Created by matrixy on 2019/4/9.
- */
-public class Jtt1078Decoder
-{
-    //4096
-    ByteHolder buffer = new ByteHolder(4096);
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 
-    public void write(byte[] block)
-    {
+/**
+ * JTT1078 Protocol Decoder
+ */
+
+public class Jtt1078Decoder {
+    private ByteHolder buffer = new ByteHolder(4096);
+    private int simLen = 0;
+    private String sim = "";
+    private byte channel = 0;
+    private static final byte[] HEAD1078 = {0x30, 0x31, 0x63, 0x64};
+
+    public void write(byte[] block) {
         buffer.write(block);
     }
 
-    public void write(byte[] block, int startIndex, int length)
-    {
+    public void write(byte[] block, int startIndex, int length) {
         byte[] buff = new byte[length];
         System.arraycopy(block, startIndex, buff, 0, length);
         write(buff);
     }
 
-    public Packet decode()
-    {
-        if (this.buffer.size() < 30) return null;
+    public Packet decode() {
+        if (buffer.size() < 4) return null;
 
-        if ((buffer.getInt(0) & 0x7fffffff) != 0x30316364)
-        {
-            String header = ByteUtils.toString(buffer.array(30));
-            throw new RuntimeException("invalid protocol header: " + header);
+        // Check header
+        if (!Arrays.equals(buffer.array(4), HEAD1078)) {
+//            log.warn("Invalid protocol header, expected:30316364, actual:{}",
+//                    ByteUtils.toHexString(buffer.array(4)));
+            buffer.clear();
+            return null;
         }
 
-        int lengthOffset = 28;
-        int dataType = (this.buffer.get(15) >> 4) & 0x0f;
-        // 閫忎紶鏁版嵁绫诲瀷锛�0100锛屾病鏈夊悗闈㈢殑鏃堕棿浠ュ強Last I Frame Interval鍜孡ast Frame Interval瀛楁
-        if (dataType == 0x04) lengthOffset = 28 - 8 - 2 - 2;
-        else if (dataType == 0x03) lengthOffset = 28 - 4;
-        int bodyLength = this.buffer.getShort(lengthOffset);
+        // Try to determine protocol version if not set
+        if (simLen == 0) {
+            // Try version 16 (2013 protocol)
+            Packet packet = decodePacket(16);
+            if (packet != null) {
+                Analyze analyze = parse16(packet.getBytes());
+                sim = analyze.getSim();
+                channel = analyze.getCh();
+                simLen = 6;
+                System.out.printf("%s-%d device started output%n", sim, channel);
+                return packet;
+            }
 
-        int packetLength = bodyLength + lengthOffset + 2;
+            // Try version 19 (2019 protocol)
+            packet = decodePacket(19);
+            if (packet != null) {
+                Analyze analyze = parse19(packet.getBytes());
+                sim = analyze.getSim();
+                channel = analyze.getCh();
+                simLen = 10;
+                System.out.printf("%s-%d device started output%n", sim, channel);
+                return packet;
+            }
 
-        if (this.buffer.size() < packetLength) return null;
-        byte[] block = new byte[packetLength];
-        this.buffer.sliceInto(block, packetLength);
-        return Packet.create(block);
+            return null;
+        } else {
+            return decodePacket(simLen == 6 ? 16 : 19);
+        }
+    }
+
+    private Packet decodePacket(int version) {
+        int dataTypeIndex = version == 16 ? 15 : 19;
+        if (buffer.size() < dataTypeIndex + 1) return null;
+
+        byte dataType = (byte) ((buffer.get(dataTypeIndex) >> 4) & 0x0F);
+        int dataLenIndex;
+
+        switch (dataType) {
+            case 3:
+                dataLenIndex = dataTypeIndex + 9;
+                break;
+            case 4:
+                dataLenIndex = dataTypeIndex + 1;
+                break;
+            default:
+                dataLenIndex = dataTypeIndex + 13;
+        }
+        //log.info("dataType:"+dataType);
+        if (buffer.size() < dataLenIndex + 2) return null;
+        int dataLen = buffer.getShort(dataLenIndex) & 0xFFFF;
+        int packetLength = dataLenIndex + 2 + dataLen;
+
+        if (buffer.size() < packetLength) return null;
+
+        // Check next packet header
+        if (buffer.size() >= packetLength + 4) {
+            byte[] nextHeader = new byte[4];
+            for (int i = 0; i < 4; i++) {
+                nextHeader[i] = buffer.get(packetLength + i);
+            }
+            if (!Arrays.equals(nextHeader, HEAD1078)) {
+               // log.warn("Invalid data type: {}", dataType);
+                buffer.clear();
+                return null;
+            }
+        }
+
+        byte[] packetData = new byte[packetLength];
+        buffer.sliceInto(packetData, packetLength);
+        // printPacketInfo(packetData, version);
+        return Packet.create(packetData);
+    }
+
+    private Analyze parse16(byte[] data) {
+        Analyze analyze = new Analyze();
+        analyze.setPt((byte) (data[5] & 0x7F));
+        analyze.setSn((short) (((data[6] & 0xFF) << 8) | (data[7] & 0xFF)));
+
+        StringBuilder simBuilder = new StringBuilder();
+        for (int i = 0; i < 6; i++) {
+            simBuilder.append(nextBcd(data, 8 + i));
+        }
+        analyze.setSim(simBuilder.toString());
+
+        analyze.setCh(data[14]);
+        analyze.setDt((byte) ((data[15] >> 4) & 0x0F));
+        analyze.setFi((byte) (data[15] & 0x0F));
+
+        int dataLenIndex;
+        switch (analyze.getDt()) {
+            case 3:
+                analyze.setTimestamp(setTimestamp(data, 16));
+                dataLenIndex = 24;
+                break;
+            case 4:
+                dataLenIndex = 16;
+                break;
+            default:
+                analyze.setTimestamp(setTimestamp(data, 16));
+                analyze.setLastIInterval((short) (((data[24] & 0xFF) << 8) | (data[25] & 0xFF)));
+                analyze.setLastInterval((short) (((data[26] & 0xFF) << 8) | (data[27] & 0xFF)));
+                dataLenIndex = 28;
+        }
+
+        analyze.setDataLen((short) (((data[dataLenIndex] & 0xFF) << 8) | (data[dataLenIndex + 1] & 0xFF)));
+        byte[] payload = new byte[analyze.getDataLen()];
+        System.arraycopy(data, dataLenIndex + 2, payload, 0, payload.length);
+        analyze.setData(payload);
+
+        return analyze;
+    }
+
+    private Analyze parse19(byte[] data) {
+        Analyze analyze = new Analyze();
+        analyze.setPt((byte) (data[5] & 0x7F));
+        analyze.setSn((short) (((data[6] & 0xFF) << 8) | (data[7] & 0xFF)));
+
+        StringBuilder simBuilder = new StringBuilder();
+        for (int i = 0; i < 10; i++) {
+            simBuilder.append(nextBcd(data, 8 + i));
+        }
+        analyze.setSim(simBuilder.toString());
+
+        analyze.setCh(data[18]);
+        analyze.setDt((byte) ((data[19] >> 4) & 0x0F));
+        analyze.setFi((byte) (data[19] & 0x0F));
+
+        int dataLenIndex;
+        switch (analyze.getDt()) {
+            case 3:
+                analyze.setTimestamp(setTimestamp(data, 20));
+                dataLenIndex = 28;
+                break;
+            case 4:
+                dataLenIndex = 20;
+                break;
+            default:
+                analyze.setTimestamp(setTimestamp(data, 20));
+                analyze.setLastIInterval((short) (((data[28] & 0xFF) << 8) | (data[29] & 0xFF)));
+                analyze.setLastInterval((short) (((data[30] & 0xFF) << 8) | (data[31] & 0xFF)));
+                dataLenIndex = 32;
+        }
+
+        analyze.setDataLen((short) (((data[dataLenIndex] & 0xFF) << 8) | (data[dataLenIndex + 1] & 0xFF)));
+        byte[] payload = new byte[analyze.getDataLen()];
+        System.arraycopy(data, dataLenIndex + 2, payload, 0, payload.length);
+        analyze.setData(payload);
+
+        return analyze;
+    }
+
+    private long setTimestamp(byte[] data, int offset) {
+        long timestamp = 0;
+        for (int i = 0; i < 8; i++) {
+            timestamp = (timestamp << 8) | (data[offset + i] & 0xFF);
+        }
+        return timestamp;
+    }
+
+    private String nextBcd(byte[] data, int offset) {
+        byte val = data[offset];
+        int ch1 = (val >> 4) & 0x0F;
+        int ch2 = val & 0x0F;
+        return String.format("%d%d", ch1, ch2);
+    }
+
+    private void printPacketInfo(byte[] packet, int version) {
+        System.out.println("\n=== JTT1078 " + (version == 16 ? "2013" : "2019") + " Protocol Packet ===");
+        System.out.println("Header: " + ByteUtils.toHexString(packet, 0, 4));
+
+        Analyze analyze = version == 16 ? parse16(packet) : parse19(packet);
+        System.out.println("Payload Type: " + analyze.getPt());
+        System.out.println("Sequence Number: " + analyze.getSn());
+        System.out.println("SIM: " + analyze.getSim());
+        System.out.println("Channel: " + analyze.getCh());
+        System.out.println("Data Type: " + getDataTypeDescription(analyze.getDt()));
+        System.out.println("Frame Info: " + getFrameInfoDescription(analyze.getFi()));
+
+        if (analyze.getTimestamp() != 0) {
+            Instant instant = Instant.ofEpochMilli(analyze.getTimestamp());
+            ZonedDateTime beijingTime = instant.atZone(ZoneOffset.ofHours(8));
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            System.out.println("Timestamp: " + formatter.format(beijingTime));
+        }
+
+        if (analyze.getLastIInterval() != 0) {
+            System.out.println("Last I Frame Interval: " + analyze.getLastIInterval());
+        }
+        if (analyze.getLastInterval() != 0) {
+            System.out.println("Last Frame Interval: " + analyze.getLastInterval());
+        }
+
+        System.out.println("Data Length: " + analyze.getDataLen());
+        System.out.println("=================================\n");
+    }
+
+    private String getDataTypeDescription(int dataType) {
+        switch (dataType) {
+            case 0: return "Video I Frame";
+            case 1: return "Video P Frame";
+            case 2: return "Video B Frame";
+            case 3: return "Audio Frame";
+            case 4: return "Transparent Data";
+            default: return "Unknown (" + dataType + ")";
+        }
+    }
+
+    private String getFrameInfoDescription(int fi) {
+        switch (fi) {
+            case 0: return "Atomic Packet";
+            case 1: return "First Packet";
+            case 2: return "Last Packet";
+            case 3: return "Middle Packet";
+            default: return "Unknown (" + fi + ")";
+        }
     }
 }
+
+class Analyze {
+    private byte pt;
+    private short sn;
+    private String sim;
+    private byte ch;
+    private byte dt;
+    private byte fi;
+    private long timestamp;
+    private short lastIInterval;
+    private short lastInterval;
+    private short dataLen;
+    private byte[] data;
+
+    // Getters and setters
+    public byte getPt() { return pt; }
+    public void setPt(byte pt) { this.pt = pt; }
+
+    public short getSn() { return sn; }
+    public void setSn(short sn) { this.sn = sn; }
+
+    public String getSim() { return sim; }
+    public void setSim(String sim) { this.sim = sim; }
+
+    public byte getCh() { return ch; }
+    public void setCh(byte ch) { this.ch = ch; }
+
+    public byte getDt() { return dt; }
+    public void setDt(byte dt) { this.dt = dt; }
+
+    public byte getFi() { return fi; }
+    public void setFi(byte fi) { this.fi = fi; }
+
+    public long getTimestamp() { return timestamp; }
+    public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
+
+    public short getLastIInterval() { return lastIInterval; }
+    public void setLastIInterval(short lastIInterval) { this.lastIInterval = lastIInterval; }
+
+    public short getLastInterval() { return lastInterval; }
+    public void setLastInterval(short lastInterval) { this.lastInterval = lastInterval; }
+
+    public short getDataLen() { return dataLen; }
+    public void setDataLen(short dataLen) { this.dataLen = dataLen; }
+
+    public byte[] getData() { return data; }
+    public void setData(byte[] data) { this.data = data; }
+}
\ No newline at end of file

--
Gitblit v1.9.3