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);
|
}
|
}
|