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