package com.anyun.h264; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.nio.ByteBuffer; import timber.log.Timber; /** * JT/T 1076-2016 协议工具类 * 提供UDP/TCP发送、SIM卡号BCD转换、RTP包创建等公共功能 */ public class JT1076ProtocolHelper { private static final String TAG = "JT1076ProtocolHelper"; // JT/T 1076-2016 RTP协议常量 public static final byte[] FRAME_HEADER = {0x30, 0x31, 0x63, 0x64}; // 帧头标识 public static final int MAX_PACKET_SIZE = 950; // 数据体最大长度 // 视频数据类型 public static final int DATA_TYPE_I_FRAME = 0x00; // I帧 public static final int DATA_TYPE_P_FRAME = 0x10; // P帧 public static final int DATA_TYPE_B_FRAME = 0x20; // B帧 // 音频数据类型 public static final int DATA_TYPE_AUDIO = 0x30; // 音频帧 // 分包处理标记 public static final int PACKET_MARK_ATOMIC = 0x00; // 原子包 public static final int PACKET_MARK_FIRST = 0x01; // 首包 public static final int PACKET_MARK_LAST = 0x02; // 末包 public static final int PACKET_MARK_MIDDLE = 0x03; // 中间包 // RTP负载类型 public static final int RTP_PAYLOAD_TYPE_VIDEO = 98; // 视频负载类型 public static final int RTP_PAYLOAD_TYPE_AUDIO = 97; // 音频负载类型 // 传输协议类型 public static final int PROTOCOL_TYPE_UDP = 0; // UDP协议 public static final int PROTOCOL_TYPE_TCP = 1; // TCP协议 // 服务器参数 private String serverIp; private int serverPort; // 协议类型(默认UDP) private int protocolType = PROTOCOL_TYPE_TCP; // UDP参数 private DatagramSocket udpSocket; private InetAddress serverAddress; // TCP参数 private JT1076TcpClient tcpClient; // RTP协议参数 private String simCardNumber = "123456789012"; // 12位SIM卡号 private byte logicalChannelNumber = 1; // 逻辑通道号 private short sequenceNumber = 0; // 包序号(自动递增) /** * 设置服务器地址 */ public void setServerAddress(String ip, int port) { this.serverIp = ip; this.serverPort = port; Timber.w("设置IP="+ip+"_port:"+port); // 如果TCP客户端已存在,更新地址 if (tcpClient != null) { tcpClient.setServerAddress(ip, port); } } /** * 设置传输协议类型(UDP或TCP) * @param protocolType PROTOCOL_TYPE_UDP 或 PROTOCOL_TYPE_TCP */ public void setProtocolType(int protocolType) { if (protocolType != PROTOCOL_TYPE_UDP && protocolType != PROTOCOL_TYPE_TCP) { Timber.w("Invalid protocol type: " + protocolType + ", using UDP"); protocolType = PROTOCOL_TYPE_TCP; } // 如果协议类型改变,先关闭旧的连接 if (this.protocolType != protocolType) { if (this.protocolType == PROTOCOL_TYPE_UDP) { closeUdpSocket(); } else { closeTcpSocket(); } } this.protocolType = protocolType; Timber.d("Protocol type set to: " + (protocolType == PROTOCOL_TYPE_UDP ? "UDP" : "TCP")); } /** * 获取当前协议类型 */ public int getProtocolType() { return protocolType; } /** * 设置SIM卡号和逻辑通道号 */ public void setProtocolParams(String simCardNumber, byte logicalChannelNumber) { this.simCardNumber = simCardNumber; this.logicalChannelNumber = logicalChannelNumber; } /** * 初始化Socket(根据协议类型自动选择UDP或TCP) */ public boolean initializeSocket() { if (protocolType == PROTOCOL_TYPE_UDP) { return initializeUdpSocket(); } else { return initializeTcpSocket(); } } /** * 初始化UDP Socket */ public boolean initializeUdpSocket() { try { if (serverIp == null || serverIp.isEmpty()) { Timber.e("Server IP not set"); return false; } udpSocket = new DatagramSocket(); serverAddress = InetAddress.getByName(serverIp); Timber.d("UDP socket initialized, target: " + serverIp + ":" + serverPort); return true; } catch (Exception e) { Timber.e(e,"Initialize UDP socket failed"); return false; } } /** * 初始化TCP Socket */ public boolean initializeTcpSocket() { try { if (serverIp == null || serverIp.isEmpty()) { Timber.e("Server IP not set"); return false; } if (tcpClient == null) { tcpClient = new JT1076TcpClient(); tcpClient.setServerAddress(serverIp, serverPort); // 设置连接状态监听器 tcpClient.setConnectionListener(new JT1076TcpClient.ConnectionListener() { @Override public void onConnected() { Timber.d("TCP connection established"); } @Override public void onDisconnected() { Timber.d( "TCP connection disconnected"); } @Override public void onError(Throwable cause) { Timber.e(cause, "TCP connection error"); } }); } tcpClient.connect(); Timber.d("TCP socket initializing, target: " + serverIp + ":" + serverPort); return true; } catch (Exception e) { Timber.e(e,"Initialize TCP socket failed"); return false; } } /** * 关闭Socket(根据协议类型自动选择) */ public void closeSocket() { if (protocolType == PROTOCOL_TYPE_UDP) { closeUdpSocket(); } else { closeTcpSocket(); } } /** * 关闭UDP Socket */ public void closeUdpSocket() { if (udpSocket != null) { try { udpSocket.close(); } catch (Exception e) { Timber.e( e,"Close UDP socket error"); } udpSocket = null; } serverAddress = null; } /** * 关闭TCP Socket */ public void closeTcpSocket() { if (tcpClient != null) { tcpClient.disconnect(); tcpClient = null; } } /** * 发送RTP包(根据协议类型自动选择UDP或TCP) */ public void sendPacket(byte[] packet) { if (protocolType == PROTOCOL_TYPE_UDP) { sendUdpPacket(packet); } else { sendTcpPacket(packet); } } /** * 发送UDP包(保持向后兼容) */ public void sendUdpPacket(byte[] packet) { try { if (udpSocket != null && serverAddress != null) { DatagramPacket datagramPacket = new DatagramPacket( packet, packet.length, serverAddress, serverPort); udpSocket.send(datagramPacket); } else { Timber.w("UDP socket not initialized"); } } catch (Exception e) { Timber.e(e,"Send UDP packet error"); } } /** * 发送TCP包 */ public void sendTcpPacket(byte[] packet) { if (tcpClient != null && tcpClient.isConnected()) { tcpClient.sendPacket(packet); } else { Timber.w("TCP socket not connected"); } } /** * 将SIM卡号转换为BCD格式(6字节) */ public byte[] convertSimToBCD(String simNumber) { byte[] bcd = new byte[6]; // 确保SIM卡号为12位数字,不足前面补0 StringBuilder sim = new StringBuilder(); if (simNumber != null) { // 只保留数字字符 String digits = simNumber.replaceAll("[^0-9]", ""); sim.append(digits); } // 补齐或截断到12位 while (sim.length() < 12) { sim.insert(0, '0'); } if (sim.length() > 12) { sim.setLength(12); } // 转换为BCD格式 String simStr = sim.toString(); for (int i = 0; i < 6; i++) { int high = Character.digit(simStr.charAt(i * 2), 10); int low = Character.digit(simStr.charAt(i * 2 + 1), 10); if (high < 0) high = 0; if (low < 0) low = 0; bcd[i] = (byte) ((high << 4) | low); } return bcd; } /** * 获取下一个包序号(自动递增) */ public short getNextSequenceNumber() { return sequenceNumber++; } /** * 重置包序号 */ public void resetSequenceNumber() { sequenceNumber = 0; } /** * 创建视频RTP包(JT/T 1076-2016协议格式) * @param dataBody 数据体 * @param timestamp 时间戳(毫秒) * @param dataType 数据类型(I帧/P帧/B帧) * @param packetMark 分包处理标记 * @param lastIFrameInterval 距上一个I帧的时间间隔(毫秒) * @param lastFrameInterval 距上一帧的时间间隔(毫秒) * @param payloadType RTP负载类型(默认96) * @return RTP包数据 */ public byte[] createVideoRtpPacket(byte[] dataBody, long timestamp, int dataType, int packetMark, long lastIFrameInterval, long lastFrameInterval, int payloadType) { // 计算包大小:帧头(4) + RTP头(2) + 序号(2) + SIM卡号(6) + 逻辑通道(1) + // 数据类型(1) + 时间戳(8) + Last I Frame Interval(2) + Last Frame Interval(2) + // 数据体长度(2) + 数据体 int packetSize = 4 + 2 + 2 + 6 + 1 + 1 + 8 + 2 + 2 + 2 + dataBody.length; ByteBuffer buffer = ByteBuffer.allocate(packetSize); buffer.order(java.nio.ByteOrder.BIG_ENDIAN); // 1. 帧头标识 (4 bytes) buffer.put(FRAME_HEADER); // 2. RTP头部 (2 bytes) // V=2 (2 bits), P=0 (1 bit), X=0 (1 bit), CC=1 (4 bits), M=0/1 (1 bit), PT (7 bits) byte rtpHeaderByte1 = (byte) 0x81; // V=2, P=0, X=0, CC=1 byte rtpHeaderByte2 = (byte) (payloadType & 0x7F); // M=0, PT buffer.put(rtpHeaderByte1); buffer.put(rtpHeaderByte2); // 3. 包序号 (2 bytes) buffer.putShort(getNextSequenceNumber()); // 4. SIM卡号 (6 bytes BCD格式) byte[] simBytes = convertSimToBCD(simCardNumber); buffer.put(simBytes, 0, 6); // 5. 逻辑通道号 (1 byte) buffer.put(logicalChannelNumber); // 6. 数据类型 + 分包处理标记 (1 byte) byte dataTypeAndMark = (byte) ((dataType & 0xF0) | (packetMark & 0x0F)); buffer.put(dataTypeAndMark); // 7. 时间戳 (8 bytes, 毫秒) buffer.putLong(timestamp); // 8. Last I Frame Interval (2 bytes, 毫秒) buffer.putShort((short) lastIFrameInterval); // 9. Last Frame Interval (2 bytes, 毫秒) buffer.putShort((short) lastFrameInterval); // 10. 数据体长度 (2 bytes) buffer.putShort((short) dataBody.length); // 11. 数据体 buffer.put(dataBody); return buffer.array(); } /** * 创建视频RTP包(使用默认视频负载类型96) */ public byte[] createVideoRtpPacket(byte[] dataBody, long timestamp, int dataType, int packetMark, long lastIFrameInterval, long lastFrameInterval) { return createVideoRtpPacket(dataBody, timestamp, dataType, packetMark, lastIFrameInterval, lastFrameInterval, RTP_PAYLOAD_TYPE_VIDEO); } /** * 创建音频RTP包(JT/T 1076-2016协议格式) * 注意:音频帧不包含Last I Frame Interval和Last Frame Interval字段 * @param dataBody 数据体 * @param timestamp 时间戳(毫秒) * @param dataType 数据类型(音频帧) * @param packetMark 分包处理标记 * @param payloadType RTP负载类型(默认97) * @return RTP包数据 */ public byte[] createAudioRtpPacket(byte[] dataBody, long timestamp, int dataType, int packetMark, int payloadType) { // 计算包大小(音频:不包含Last I Frame Interval和Last Frame Interval) // 帧头(4) + RTP头(2) + 序号(2) + SIM卡号(6) + 逻辑通道(1) + // 数据类型(1) + 时间戳(8) + 数据体长度(2) + 数据体 int packetSize = 4 + 2 + 2 + 6 + 1 + 1 + 8 + 2 + dataBody.length; ByteBuffer buffer = ByteBuffer.allocate(packetSize); buffer.order(java.nio.ByteOrder.BIG_ENDIAN); // 1. 帧头标识 (4 bytes) buffer.put(FRAME_HEADER); // 2. RTP头部 (2 bytes) // V=2 (2 bits), P=0 (1 bit), X=0 (1 bit), CC=1 (4 bits), M=0/1 (1 bit), PT (7 bits) byte rtpHeaderByte1 = (byte) 0x81; // V=2, P=0, X=0, CC=1 byte rtpHeaderByte2 = (byte) (payloadType & 0x7F); // M=0, PT buffer.put(rtpHeaderByte1); buffer.put(rtpHeaderByte2); // 3. 包序号 (2 bytes) buffer.putShort(getNextSequenceNumber()); // 4. SIM卡号 (6 bytes BCD格式) byte[] simBytes = convertSimToBCD(simCardNumber); buffer.put(simBytes, 0, 6); // 5. 逻辑通道号 (1 byte) buffer.put(logicalChannelNumber); // 6. 数据类型 + 分包处理标记 (1 byte) byte dataTypeAndMark = (byte) ((dataType & 0xF0) | (packetMark & 0x0F)); buffer.put(dataTypeAndMark); // 7. 时间戳 (8 bytes, 毫秒) buffer.putLong(timestamp); // 注意:音频帧不包含Last I Frame Interval和Last Frame Interval字段 // 8. 数据体长度 (2 bytes) buffer.putShort((short) dataBody.length); // 9. 数据体 buffer.put(dataBody); return buffer.array(); } /** * 创建音频RTP包(使用默认音频负载类型97) */ public byte[] createAudioRtpPacket(byte[] dataBody, long timestamp, int dataType, int packetMark) { return createAudioRtpPacket(dataBody, timestamp, dataType, packetMark, RTP_PAYLOAD_TYPE_AUDIO); } }