From c795d1c28858b3300ad43792d58cfe825961f06d Mon Sep 17 00:00:00 2001
From: Dana <Dana_Lee1016@126.com>
Date: 星期日, 30 十一月 2025 11:16:01 +0800
Subject: [PATCH] 1.tcp udp 切换
---
app/src/main/java/com/anyun/h264/MainActivity.kt | 4
app/src/main/java/com/anyun/h264/H264Encoder.java | 13
app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java | 150 ++++++++++++++++++
app/src/main/java/com/anyun/h264/JT1076TcpClient.java | 257 ++++++++++++++++++++++++++++++++
app/src/main/java/com/anyun/h264/AACEncoder.java | 12
5 files changed, 418 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/com/anyun/h264/AACEncoder.java b/app/src/main/java/com/anyun/h264/AACEncoder.java
index 0a07d82..e5086e4 100644
--- a/app/src/main/java/com/anyun/h264/AACEncoder.java
+++ b/app/src/main/java/com/anyun/h264/AACEncoder.java
@@ -111,8 +111,8 @@
// 2. 鍒濆鍖朅AC缂栫爜鍣�
initEncoder();
- // 3. 鍒濆鍖朥DP Socket
- if (!protocolHelper.initializeUdpSocket()) {
+ // 3. 鍒濆鍖朣ocket锛圲DP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+ if (!protocolHelper.initializeSocket()) {
return false;
}
@@ -287,8 +287,8 @@
byte[] rtpPacket = protocolHelper.createAudioRtpPacket(
packetData, timestamp, JT1076ProtocolHelper.DATA_TYPE_AUDIO, packetMark);
- // 鍙戦�乁DP鍖�
- protocolHelper.sendUdpPacket(rtpPacket);
+ // 鍙戦�丷TP鍖咃紙UDP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+ protocolHelper.sendPacket(rtpPacket);
offset += packetDataSize;
}
@@ -343,9 +343,9 @@
audioRecord = null;
}
- // 鍏抽棴UDP Socket
+ // 鍏抽棴Socket锛圲DP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
if (protocolHelper != null) {
- protocolHelper.closeUdpSocket();
+ protocolHelper.closeSocket();
}
Log.d(TAG, "AAC encoder stopped");
diff --git a/app/src/main/java/com/anyun/h264/H264Encoder.java b/app/src/main/java/com/anyun/h264/H264Encoder.java
index 9228d29..bd233ee 100644
--- a/app/src/main/java/com/anyun/h264/H264Encoder.java
+++ b/app/src/main/java/com/anyun/h264/H264Encoder.java
@@ -83,6 +83,7 @@
public H264Encoder() {
this.usbCamera = new UsbCamera();
this.protocolHelper = new JT1076ProtocolHelper();
+ protocolHelper.setProtocolType(JT1076ProtocolHelper.PROTOCOL_TYPE_TCP);
}
/**
@@ -178,8 +179,8 @@
// 3. 鍒濆鍖朒264缂栫爜鍣�
initEncoder();
- // 4. 鍒濆鍖朥DP Socket
- if (!protocolHelper.initializeUdpSocket()) {
+ // 4. 鍒濆鍖朣ocket锛圲DP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+ if (!protocolHelper.initializeSocket()) {
return false;
}
@@ -502,8 +503,8 @@
packetData, timestamp, dataType, packetMark,
lastIFrameInterval, lastFrameInterval);
- // 鍙戦�乁DP鍖�
- protocolHelper.sendUdpPacket(rtpPacket);
+ // 鍙戦�丷TP鍖咃紙UDP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+ protocolHelper.sendPacket(rtpPacket);
offset += packetDataSize;
}
@@ -548,9 +549,9 @@
}
}
- // 鍏抽棴UDP Socket
+ // 鍏抽棴Socket锛圲DP鎴朤CP锛屾牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
if (protocolHelper != null) {
- protocolHelper.closeUdpSocket();
+ protocolHelper.closeSocket();
}
// 鍏抽棴鏂囦欢杈撳嚭
diff --git a/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java b/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
index 0613a1c..1489907 100644
--- a/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
+++ b/app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
@@ -10,7 +10,7 @@
/**
* JT/T 1076-2016 鍗忚宸ュ叿绫�
- * 鎻愪緵UDP鍙戦�併�丼IM鍗″彿BCD杞崲銆丷TP鍖呭垱寤虹瓑鍏叡鍔熻兘
+ * 鎻愪緵UDP/TCP鍙戦�併�丼IM鍗″彿BCD杞崲銆丷TP鍖呭垱寤虹瓑鍏叡鍔熻兘
*/
public class JT1076ProtocolHelper {
private static final String TAG = "JT1076ProtocolHelper";
@@ -37,11 +37,23 @@
public static final int RTP_PAYLOAD_TYPE_VIDEO = 96; // 瑙嗛璐熻浇绫诲瀷
public static final int RTP_PAYLOAD_TYPE_AUDIO = 97; // 闊抽璐熻浇绫诲瀷
- // UDP鍙傛暟
+ // 浼犺緭鍗忚绫诲瀷
+ public static final int PROTOCOL_TYPE_UDP = 0; // UDP鍗忚
+ public static final int PROTOCOL_TYPE_TCP = 1; // TCP鍗忚
+
+ // 鏈嶅姟鍣ㄥ弬鏁�
private String serverIp;
private int serverPort;
+
+ // 鍗忚绫诲瀷锛堥粯璁DP锛�
+ private int protocolType = PROTOCOL_TYPE_UDP;
+
+ // UDP鍙傛暟
private DatagramSocket udpSocket;
private InetAddress serverAddress;
+
+ // TCP鍙傛暟
+ private JT1076TcpClient tcpClient;
// RTP鍗忚鍙傛暟
private String simCardNumber = "123456789012"; // 12浣峉IM鍗″彿
@@ -49,11 +61,45 @@
private short sequenceNumber = 0; // 鍖呭簭鍙凤紙鑷姩閫掑锛�
/**
- * 璁剧疆UDP鏈嶅姟鍣ㄥ湴鍧�
+ * 璁剧疆鏈嶅姟鍣ㄥ湴鍧�
*/
public void setServerAddress(String ip, int port) {
this.serverIp = ip;
this.serverPort = port;
+ // 濡傛灉TCP瀹㈡埛绔凡瀛樺湪锛屾洿鏂板湴鍧�
+ if (tcpClient != null) {
+ tcpClient.setServerAddress(ip, port);
+ }
+ }
+
+ /**
+ * 璁剧疆浼犺緭鍗忚绫诲瀷锛圲DP鎴朤CP锛�
+ * @param protocolType PROTOCOL_TYPE_UDP 鎴� PROTOCOL_TYPE_TCP
+ */
+ public void setProtocolType(int protocolType) {
+ if (protocolType != PROTOCOL_TYPE_UDP && protocolType != PROTOCOL_TYPE_TCP) {
+ Log.w(TAG, "Invalid protocol type: " + protocolType + ", using UDP");
+ protocolType = PROTOCOL_TYPE_UDP;
+ }
+
+ // 濡傛灉鍗忚绫诲瀷鏀瑰彉锛屽厛鍏抽棴鏃х殑杩炴帴
+ if (this.protocolType != protocolType) {
+ if (this.protocolType == PROTOCOL_TYPE_UDP) {
+ closeUdpSocket();
+ } else {
+ closeTcpSocket();
+ }
+ }
+
+ this.protocolType = protocolType;
+ Log.d(TAG, "Protocol type set to: " + (protocolType == PROTOCOL_TYPE_UDP ? "UDP" : "TCP"));
+ }
+
+ /**
+ * 鑾峰彇褰撳墠鍗忚绫诲瀷
+ */
+ public int getProtocolType() {
+ return protocolType;
}
/**
@@ -62,6 +108,17 @@
public void setProtocolParams(String simCardNumber, byte logicalChannelNumber) {
this.simCardNumber = simCardNumber;
this.logicalChannelNumber = logicalChannelNumber;
+ }
+
+ /**
+ * 鍒濆鍖朣ocket锛堟牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨UDP鎴朤CP锛�
+ */
+ public boolean initializeSocket() {
+ if (protocolType == PROTOCOL_TYPE_UDP) {
+ return initializeUdpSocket();
+ } else {
+ return initializeTcpSocket();
+ }
}
/**
@@ -85,6 +142,59 @@
}
/**
+ * 鍒濆鍖朤CP Socket
+ */
+ public boolean initializeTcpSocket() {
+ try {
+ if (serverIp == null || serverIp.isEmpty()) {
+ Log.e(TAG, "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() {
+ Log.d(TAG, "TCP connection established");
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.d(TAG, "TCP connection disconnected");
+ }
+
+ @Override
+ public void onError(Throwable cause) {
+ Log.e(TAG, "TCP connection error", cause);
+ }
+ });
+ }
+
+ tcpClient.connect();
+ Log.d(TAG, "TCP socket initializing, target: " + serverIp + ":" + serverPort);
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Initialize TCP socket failed", e);
+ return false;
+ }
+ }
+
+ /**
+ * 鍏抽棴Socket锛堟牴鎹崗璁被鍨嬭嚜鍔ㄩ�夋嫨锛�
+ */
+ public void closeSocket() {
+ if (protocolType == PROTOCOL_TYPE_UDP) {
+ closeUdpSocket();
+ } else {
+ closeTcpSocket();
+ }
+ }
+
+ /**
* 鍏抽棴UDP Socket
*/
public void closeUdpSocket() {
@@ -100,7 +210,28 @@
}
/**
- * 鍙戦�乁DP鍖�
+ * 鍏抽棴TCP Socket
+ */
+ public void closeTcpSocket() {
+ if (tcpClient != null) {
+ tcpClient.disconnect();
+ tcpClient = null;
+ }
+ }
+
+ /**
+ * 鍙戦�丷TP鍖咃紙鏍规嵁鍗忚绫诲瀷鑷姩閫夋嫨UDP鎴朤CP锛�
+ */
+ public void sendPacket(byte[] packet) {
+ if (protocolType == PROTOCOL_TYPE_UDP) {
+ sendUdpPacket(packet);
+ } else {
+ sendTcpPacket(packet);
+ }
+ }
+
+ /**
+ * 鍙戦�乁DP鍖咃紙淇濇寔鍚戝悗鍏煎锛�
*/
public void sendUdpPacket(byte[] packet) {
try {
@@ -117,6 +248,17 @@
}
/**
+ * 鍙戦�乀CP鍖�
+ */
+ public void sendTcpPacket(byte[] packet) {
+ if (tcpClient != null && tcpClient.isConnected()) {
+ tcpClient.sendPacket(packet);
+ } else {
+ Log.w(TAG, "TCP socket not connected");
+ }
+ }
+
+ /**
* 灏哠IM鍗″彿杞崲涓築CD鏍煎紡锛�6瀛楄妭锛�
*/
public byte[] convertSimToBCD(String simNumber) {
diff --git a/app/src/main/java/com/anyun/h264/JT1076TcpClient.java b/app/src/main/java/com/anyun/h264/JT1076TcpClient.java
new file mode 100644
index 0000000..58747ce
--- /dev/null
+++ b/app/src/main/java/com/anyun/h264/JT1076TcpClient.java
@@ -0,0 +1,257 @@
+package com.anyun.h264;
+
+import android.util.Log;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPromise;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * JT/T 1076-2016 TCP瀹㈡埛绔伐鍏风被
+ * 浣跨敤Netty瀹炵幇TCP杩炴帴鍜孯TP鍖呭彂閫�
+ */
+public class JT1076TcpClient {
+ private static final String TAG = "JT1076TcpClient";
+
+ private String serverIp;
+ private int serverPort;
+ private EventLoopGroup workerGroup;
+ private Channel channel;
+ private boolean isConnected = false;
+
+ // 杩炴帴鐘舵�佸洖璋冩帴鍙�
+ public interface ConnectionListener {
+ void onConnected();
+ void onDisconnected();
+ void onError(Throwable cause);
+ }
+
+ private ConnectionListener connectionListener;
+
+ /**
+ * 璁剧疆鏈嶅姟鍣ㄥ湴鍧�
+ */
+ public void setServerAddress(String ip, int port) {
+ this.serverIp = ip;
+ this.serverPort = port;
+ }
+
+ /**
+ * 璁剧疆杩炴帴鐘舵�佺洃鍚櫒
+ */
+ public void setConnectionListener(ConnectionListener listener) {
+ this.connectionListener = listener;
+ }
+
+ /**
+ * 鍒濆鍖朤CP杩炴帴锛堝紓姝ワ級
+ */
+ public void connect() {
+ if (serverIp == null || serverIp.isEmpty()) {
+ Log.e(TAG, "Server IP not set");
+ if (connectionListener != null) {
+ connectionListener.onError(new IllegalArgumentException("Server IP not set"));
+ }
+ return;
+ }
+
+ if (workerGroup != null) {
+ Log.w(TAG, "TCP client already initialized, disconnecting first");
+ disconnect();
+ }
+
+ // 鍒涘缓EventLoopGroup锛堜娇鐢ㄥ崟绾跨▼缁勫嵆鍙級
+ workerGroup = new NioEventLoopGroup(1);
+
+ try {
+ Bootstrap bootstrap = new Bootstrap();
+ bootstrap.group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.TCP_NODELAY, true) // 绂佺敤Nagle绠楁硶锛岄檷浣庡欢杩�
+ .option(ChannelOption.SO_KEEPALIVE, true) // 鍚敤TCP keepalive
+ .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) // 杩炴帴瓒呮椂5绉�
+ .handler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline().addLast(new TcpClientHandler());
+ }
+ });
+
+ Log.d(TAG, "Connecting to TCP server: " + serverIp + ":" + serverPort);
+
+ // 寮傛杩炴帴
+ ChannelFuture future = bootstrap.connect(serverIp, serverPort);
+ future.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (future.isSuccess()) {
+ channel = future.channel();
+ isConnected = true;
+ Log.d(TAG, "TCP connection established: " + serverIp + ":" + serverPort);
+ if (connectionListener != null) {
+ connectionListener.onConnected();
+ }
+ } else {
+ isConnected = false;
+ Throwable cause = future.cause();
+ Log.e(TAG, "TCP connection failed: " + serverIp + ":" + serverPort, cause);
+ if (connectionListener != null) {
+ connectionListener.onError(cause);
+ }
+ // 杩炴帴澶辫触鏃舵竻鐞嗚祫婧�
+ shutdown();
+ }
+ }
+ });
+
+ } catch (Exception e) {
+ Log.e(TAG, "Initialize TCP client failed", e);
+ isConnected = false;
+ if (connectionListener != null) {
+ connectionListener.onError(e);
+ }
+ shutdown();
+ }
+ }
+
+ /**
+ * 鍙戦�丷TP鍖咃紙TCP鏂瑰紡锛�
+ */
+ public void sendPacket(byte[] packet) {
+ if (!isConnected || channel == null || !channel.isActive()) {
+ Log.w(TAG, "TCP channel not connected, packet dropped");
+ return;
+ }
+
+ try {
+ // 灏嗗瓧鑺傛暟缁勫寘瑁呬负ByteBuf
+ ByteBuf buffer = Unpooled.wrappedBuffer(packet);
+
+ // 寮傛鍐欏叆
+ ChannelFuture future = channel.writeAndFlush(buffer);
+
+ // 鍙�夛細鐩戝惉鍙戦�佺粨鏋�
+ future.addListener(new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture future) throws Exception {
+ if (!future.isSuccess()) {
+ Log.e(TAG, "Send TCP packet failed", future.cause());
+ }
+ }
+ });
+
+ } catch (Exception e) {
+ Log.e(TAG, "Send TCP packet error", e);
+ }
+ }
+
+ /**
+ * 鏂紑TCP杩炴帴
+ */
+ public void disconnect() {
+ isConnected = false;
+
+ if (channel != null) {
+ try {
+ ChannelFuture future = channel.close();
+ future.await(2, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Close channel interrupted", e);
+ Thread.currentThread().interrupt();
+ } catch (Exception e) {
+ Log.e(TAG, "Close channel error", e);
+ }
+ channel = null;
+ }
+
+ shutdown();
+
+ if (connectionListener != null) {
+ connectionListener.onDisconnected();
+ }
+
+ Log.d(TAG, "TCP connection closed");
+ }
+
+ /**
+ * 鍏抽棴EventLoopGroup锛堟竻鐞嗚祫婧愶級
+ */
+ private void shutdown() {
+ if (workerGroup != null) {
+ try {
+ workerGroup.shutdownGracefully().await(3, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "Shutdown worker group interrupted", e);
+ Thread.currentThread().interrupt();
+ } catch (Exception e) {
+ Log.e(TAG, "Shutdown worker group error", e);
+ }
+ workerGroup = null;
+ }
+ }
+
+ /**
+ * 妫�鏌ユ槸鍚﹀凡杩炴帴
+ */
+ public boolean isConnected() {
+ return isConnected && channel != null && channel.isActive();
+ }
+
+ /**
+ * TCP瀹㈡埛绔�氶亾澶勭悊鍣�
+ */
+ private class TcpClientHandler extends ChannelInboundHandlerAdapter {
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ Log.d(TAG, "TCP channel active: " + ctx.channel().remoteAddress());
+ isConnected = true;
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ Log.d(TAG, "TCP channel inactive: " + ctx.channel().remoteAddress());
+ isConnected = false;
+ channel = null;
+ if (connectionListener != null) {
+ connectionListener.onDisconnected();
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+ Log.e(TAG, "TCP channel exception", cause);
+ isConnected = false;
+ if (connectionListener != null) {
+ connectionListener.onError(cause);
+ }
+ ctx.close();
+ }
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ // 濡傛灉鏈嶅姟鍣ㄦ湁鍝嶅簲锛屽彲浠ュ湪杩欓噷澶勭悊
+ // 瀵逛簬JT/T 1076-2016 RTP鍙戦�侊紝閫氬父涓嶉渶瑕佸鐞嗗搷搴�
+ ByteBuf buf = (ByteBuf) msg;
+ Log.d(TAG, "Received data from server: " + buf.readableBytes() + " bytes");
+ buf.release(); // 閲婃斁ByteBuf
+ }
+ }
+}
+
diff --git a/app/src/main/java/com/anyun/h264/MainActivity.kt b/app/src/main/java/com/anyun/h264/MainActivity.kt
index 5de8803..a8cd0a3 100644
--- a/app/src/main/java/com/anyun/h264/MainActivity.kt
+++ b/app/src/main/java/com/anyun/h264/MainActivity.kt
@@ -69,8 +69,8 @@
h264Encoder?.setEnableFileOutput(true) // 鍚敤鏂囦欢杈撳嚭
// 璁剧疆UDP鏈嶅姟鍣ㄥ湴鍧�锛堝彲閫夛級
- h264Encoder?.setServerAddress("192.168.1.100", 8888)
- h264Encoder?.setProtocolParams("123456789012", 1)
+ h264Encoder?.setServerAddress("58.48.93.67", 11935)
+ h264Encoder?.setProtocolParams("013120122580", 1)
// 鍒濆鍖栧苟鍚姩
val cameraIdRange = intArrayOf(1, 2)
--
Gitblit v1.8.0