package com.anyun.h264; import android.util.Log; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.nio.ByteBuffer; /** * JT/T 1076-2016 协议工具类 * 提供UDP发送、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 = 96; // 视频负载类型 public static final int RTP_PAYLOAD_TYPE_AUDIO = 97; // 音频负载类型 // UDP参数 private String serverIp; private int serverPort; private DatagramSocket udpSocket; private InetAddress serverAddress; // RTP协议参数 private String simCardNumber = "123456789012"; // 12位SIM卡号 private byte logicalChannelNumber = 1; // 逻辑通道号 private short sequenceNumber = 0; // 包序号(自动递增) /** * 设置UDP服务器地址 */ public void setServerAddress(String ip, int port) { this.serverIp = ip; this.serverPort = port; } /** * 设置SIM卡号和逻辑通道号 */ public void setProtocolParams(String simCardNumber, byte logicalChannelNumber) { this.simCardNumber = simCardNumber; this.logicalChannelNumber = logicalChannelNumber; } /** * 初始化UDP Socket */ public boolean initializeUdpSocket() { try { if (serverIp == null || serverIp.isEmpty()) { Log.e(TAG, "Server IP not set"); return false; } udpSocket = new DatagramSocket(); serverAddress = InetAddress.getByName(serverIp); Log.d(TAG, "UDP socket initialized, target: " + serverIp + ":" + serverPort); return true; } catch (Exception e) { Log.e(TAG, "Initialize UDP socket failed", e); return false; } } /** * 关闭UDP Socket */ public void closeUdpSocket() { if (udpSocket != null) { try { udpSocket.close(); } catch (Exception e) { Log.e(TAG, "Close UDP socket error", e); } udpSocket = null; } serverAddress = null; } /** * 发送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 { Log.w(TAG, "UDP socket not initialized"); } } catch (Exception e) { Log.e(TAG, "Send UDP packet error", e); } } /** * 将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); } }