| | |
| | | package com.anyun.h264; |
| | | |
| | | import android.util.Log; |
| | | |
| | | import com.anyun.h264.util.BytesUtils; |
| | | |
| | |
| | | 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 timber.log.Timber; |
| | | |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | |
| | | */ |
| | | public void connect() { |
| | | if (serverIp == null || serverIp.isEmpty()) { |
| | | Log.e(TAG, "Server IP not set"); |
| | | Timber.e( "Server IP not set"); |
| | | if (connectionListener != null) { |
| | | connectionListener.onError(new IllegalArgumentException("Server IP not set")); |
| | | } |
| | |
| | | } |
| | | |
| | | if (workerGroup != null) { |
| | | Log.w(TAG, "TCP client already initialized, disconnecting first"); |
| | | Timber.w("TCP client already initialized, disconnecting first"); |
| | | disconnect(); |
| | | } |
| | | |
| | |
| | | } |
| | | }); |
| | | |
| | | Log.d(TAG, "Connecting to TCP server: " + serverIp + ":" + serverPort); |
| | | Timber.d("Connecting to TCP server: " + serverIp + ":" + serverPort); |
| | | |
| | | // 异步连接 |
| | | ChannelFuture future = bootstrap.connect(serverIp, serverPort); |
| | |
| | | if (future.isSuccess()) { |
| | | channel = future.channel(); |
| | | isConnected = true; |
| | | Log.d(TAG, "TCP connection established: " + serverIp + ":" + serverPort); |
| | | Timber.d("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); |
| | | Timber.e(cause, "TCP connection failed: " + serverIp + ":" + serverPort); |
| | | if (connectionListener != null) { |
| | | connectionListener.onError(cause); |
| | | } |
| | |
| | | }); |
| | | |
| | | } catch (Exception e) { |
| | | Log.e(TAG, "Initialize TCP client failed", e); |
| | | Timber.e(e, "Initialize TCP client failed"); |
| | | isConnected = false; |
| | | if (connectionListener != null) { |
| | | connectionListener.onError(e); |
| | |
| | | */ |
| | | public void sendPacket(byte[] packet) { |
| | | if (!isConnected || channel == null || !channel.isActive()) { |
| | | Log.w(TAG, "TCP channel not connected, packet dropped"); |
| | | Timber.w( "TCP channel not connected, packet dropped"); |
| | | return; |
| | | } |
| | | |
| | |
| | | // 将字节数组包装为ByteBuf |
| | | ByteBuf buffer = Unpooled.wrappedBuffer(packet); |
| | | String str = BytesUtils.bytesToHexString( BytesUtils.subArray(buffer.array(),0,30)); |
| | | Log.i(TAG, "Send TCP packet:"+ str); |
| | | Timber.i( "Send TCP packet:"+ str); |
| | | // 异步写入 |
| | | ChannelFuture future = channel.writeAndFlush(buffer); |
| | | |
| | |
| | | @Override |
| | | public void operationComplete(ChannelFuture future) throws Exception { |
| | | if (!future.isSuccess()) { |
| | | Log.e(TAG, "Send TCP packet failed", future.cause()); |
| | | Timber.e(future.cause(), "Send TCP packet failed"); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | } catch (Exception e) { |
| | | Log.e(TAG, "Send TCP packet error", e); |
| | | Timber.e(e, "Send TCP packet error"); |
| | | } |
| | | } |
| | | |
| | |
| | | ChannelFuture future = channel.close(); |
| | | future.await(2, TimeUnit.SECONDS); |
| | | } catch (InterruptedException e) { |
| | | Log.w(TAG, "Close channel interrupted", e); |
| | | Timber.w(e,"Close channel interrupted"); |
| | | Thread.currentThread().interrupt(); |
| | | } catch (Exception e) { |
| | | Log.e(TAG, "Close channel error", e); |
| | | Timber.e(e, "Close channel error"); |
| | | } |
| | | channel = null; |
| | | } |
| | |
| | | connectionListener.onDisconnected(); |
| | | } |
| | | |
| | | Log.d(TAG, "TCP connection closed"); |
| | | Timber.d("TCP connection closed"); |
| | | } |
| | | |
| | | /** |
| | |
| | | try { |
| | | workerGroup.shutdownGracefully().await(3, TimeUnit.SECONDS); |
| | | } catch (InterruptedException e) { |
| | | Log.w(TAG, "Shutdown worker group interrupted", e); |
| | | Timber.w(e, "Shutdown worker group interrupted"); |
| | | Thread.currentThread().interrupt(); |
| | | } catch (Exception e) { |
| | | Log.e(TAG, "Shutdown worker group error", e); |
| | | Timber.e(e, "Shutdown worker group error"); |
| | | } |
| | | workerGroup = null; |
| | | } |
| | |
| | | @Override |
| | | public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| | | super.channelActive(ctx); |
| | | Log.d(TAG, "TCP channel active: " + ctx.channel().remoteAddress()); |
| | | Timber.d("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()); |
| | | Timber.d("TCP channel inactive: " + ctx.channel().remoteAddress()); |
| | | isConnected = false; |
| | | channel = null; |
| | | if (connectionListener != null) { |
| | |
| | | |
| | | @Override |
| | | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| | | Log.e(TAG, "TCP channel exception", cause); |
| | | Timber.e(cause, "TCP channel exception"); |
| | | isConnected = false; |
| | | if (connectionListener != null) { |
| | | connectionListener.onError(cause); |
| | |
| | | // 如果服务器有响应,可以在这里处理 |
| | | // 对于JT/T 1076-2016 RTP发送,通常不需要处理响应 |
| | | ByteBuf buf = (ByteBuf) msg; |
| | | Log.d(TAG, "Received data from server: " + buf.readableBytes() + " bytes"); |
| | | Timber.d("Received data from server: " + buf.readableBytes() + " bytes"); |
| | | buf.release(); // 释放ByteBuf |
| | | } |
| | | } |