| app/src/main/java/com/anyun/h264/AACEncoder.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/H264Encoder.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/JT1076TcpClient.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| app/src/main/java/com/anyun/h264/MainActivity.kt | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
app/src/main/java/com/anyun/h264/AACEncoder.java
@@ -111,8 +111,8 @@ // 2. 初始化AAC编码器 initEncoder(); // 3. 初始化UDP Socket if (!protocolHelper.initializeUdpSocket()) { // 3. 初始化Socket(UDP或TCP,根据协议类型自动选择) if (!protocolHelper.initializeSocket()) { return false; } @@ -287,8 +287,8 @@ byte[] rtpPacket = protocolHelper.createAudioRtpPacket( packetData, timestamp, JT1076ProtocolHelper.DATA_TYPE_AUDIO, packetMark); // 发送UDP包 protocolHelper.sendUdpPacket(rtpPacket); // 发送RTP包(UDP或TCP,根据协议类型自动选择) protocolHelper.sendPacket(rtpPacket); offset += packetDataSize; } @@ -343,9 +343,9 @@ audioRecord = null; } // 关闭UDP Socket // 关闭Socket(UDP或TCP,根据协议类型自动选择) if (protocolHelper != null) { protocolHelper.closeUdpSocket(); protocolHelper.closeSocket(); } Log.d(TAG, "AAC encoder stopped"); 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. 初始化H264编码器 initEncoder(); // 4. 初始化UDP Socket if (!protocolHelper.initializeUdpSocket()) { // 4. 初始化Socket(UDP或TCP,根据协议类型自动选择) if (!protocolHelper.initializeSocket()) { return false; } @@ -502,8 +503,8 @@ packetData, timestamp, dataType, packetMark, lastIFrameInterval, lastFrameInterval); // 发送UDP包 protocolHelper.sendUdpPacket(rtpPacket); // 发送RTP包(UDP或TCP,根据协议类型自动选择) protocolHelper.sendPacket(rtpPacket); offset += packetDataSize; } @@ -548,9 +549,9 @@ } } // 关闭UDP Socket // 关闭Socket(UDP或TCP,根据协议类型自动选择) if (protocolHelper != null) { protocolHelper.closeUdpSocket(); protocolHelper.closeSocket(); } // 关闭文件输出 app/src/main/java/com/anyun/h264/JT1076ProtocolHelper.java
@@ -10,7 +10,7 @@ /** * JT/T 1076-2016 协议工具类 * 提供UDP发送、SIM卡号BCD转换、RTP包创建等公共功能 * 提供UDP/TCP发送、SIM卡号BCD转换、RTP包创建等公共功能 */ 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; // 协议类型(默认UDP) private int protocolType = PROTOCOL_TYPE_UDP; // UDP参数 private DatagramSocket udpSocket; private InetAddress serverAddress; // TCP参数 private JT1076TcpClient tcpClient; // RTP协议参数 private String simCardNumber = "123456789012"; // 12位SIM卡号 @@ -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); } } /** * 设置传输协议类型(UDP或TCP) * @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; } /** * 初始化Socket(根据协议类型自动选择UDP或TCP) */ public boolean initializeSocket() { if (protocolType == PROTOCOL_TYPE_UDP) { return initializeUdpSocket(); } else { return initializeTcpSocket(); } } /** @@ -85,6 +142,59 @@ } /** * 初始化TCP 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 @@ } /** * 发送UDP包 * 关闭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 { @@ -117,6 +248,17 @@ } /** * 发送TCP包 */ public void sendTcpPacket(byte[] packet) { if (tcpClient != null && tcpClient.isConnected()) { tcpClient.sendPacket(packet); } else { Log.w(TAG, "TCP socket not connected"); } } /** * 将SIM卡号转换为BCD格式(6字节) */ public byte[] convertSimToBCD(String simNumber) { app/src/main/java/com/anyun/h264/JT1076TcpClient.java
New file @@ -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连接和RTP包发送 */ 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; } /** * 初始化TCP连接(异步) */ 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(); } } /** * 发送RTP包(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 } } } 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)