331 lines
11 KiB
Java
331 lines
11 KiB
Java
package com.aros.apron.manager;
|
||
|
||
import android.os.Handler;
|
||
import android.os.Looper;
|
||
|
||
import androidx.annotation.NonNull;
|
||
|
||
import com.aros.apron.tools.LogUtil;
|
||
|
||
import java.io.IOException;
|
||
import java.net.DatagramPacket;
|
||
import java.net.DatagramSocket;
|
||
import java.net.InetAddress;
|
||
import java.net.SocketException;
|
||
import java.net.UnknownHostException;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.concurrent.Executors;
|
||
import java.util.concurrent.atomic.AtomicBoolean;
|
||
|
||
import dji.sdk.keyvalue.value.common.ComponentIndexType;
|
||
import dji.v5.manager.datacenter.MediaDataCenter;
|
||
import dji.v5.manager.datacenter.camera.StreamInfo;
|
||
import dji.v5.manager.interfaces.ICameraStreamManager;
|
||
|
||
/**
|
||
* UDP视频流推送管理类
|
||
* 负责从视频监听器获取码流数据并通过UDP协议推送
|
||
*/
|
||
public class UdpStreamManager {
|
||
private static final String TAG = "UdpStreamManager";
|
||
|
||
// 线程池和主线程Handler
|
||
private final ExecutorService udpExecutor = Executors.newSingleThreadExecutor();
|
||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||
|
||
// 单例模式
|
||
private static class UdpStreamHolder {
|
||
private static final UdpStreamManager INSTANCE = new UdpStreamManager();
|
||
}
|
||
|
||
public static UdpStreamManager getInstance() {
|
||
return UdpStreamHolder.INSTANCE;
|
||
}
|
||
|
||
// UDP配置
|
||
private static final String DEFAULT_HOST = "127.0.0.1";
|
||
private static final int DEFAULT_PORT_1_PORT = 8080;
|
||
private static final int DEFAULT_FPV_PORT = 8081;
|
||
private String targetHost = DEFAULT_HOST;
|
||
private int port1Port = DEFAULT_PORT_1_PORT;
|
||
private int fpvPort = DEFAULT_FPV_PORT;
|
||
|
||
// 状态管理
|
||
private final AtomicBoolean isStreaming = new AtomicBoolean(false);
|
||
private final AtomicBoolean isInitialized = new AtomicBoolean(false);
|
||
|
||
// UDP相关
|
||
private DatagramSocket port1UdpSocket;
|
||
private DatagramSocket fpvUdpSocket;
|
||
private InetAddress targetAddress;
|
||
|
||
// 视频码流监听器
|
||
private ICameraStreamManager.ReceiveStreamListener port1StreamListener;
|
||
private ICameraStreamManager.ReceiveStreamListener fpvStreamListener;
|
||
|
||
private UdpStreamManager() {
|
||
}
|
||
|
||
/**
|
||
* 初始化UDP流管理器
|
||
* @param host 目标主机地址
|
||
* @param port1Port PORT_1相机的目标端口
|
||
* @param fpvPort FPV相机的目标端口
|
||
*/
|
||
public void init(String host, int port1Port, int fpvPort) {
|
||
if (isInitialized.get()) {
|
||
LogUtil.log(TAG, "UDP流管理器已初始化");
|
||
return;
|
||
}
|
||
|
||
targetHost = host != null ? host : DEFAULT_HOST;
|
||
this.port1Port = port1Port > 0 ? port1Port : DEFAULT_PORT_1_PORT;
|
||
this.fpvPort = fpvPort > 0 ? fpvPort : DEFAULT_FPV_PORT;
|
||
|
||
udpExecutor.execute(() -> {
|
||
try {
|
||
// 初始化UDP socket
|
||
port1UdpSocket = new DatagramSocket();
|
||
fpvUdpSocket = new DatagramSocket();
|
||
targetAddress = InetAddress.getByName(targetHost);
|
||
|
||
// 初始化视频码流监听器
|
||
initStreamListeners();
|
||
|
||
isInitialized.set(true);
|
||
LogUtil.log(TAG, "UDP流管理器初始化成功,目标地址: " + targetHost + ", PORT_1端口: " + this.port1Port + ", FPV端口: " + this.fpvPort);
|
||
} catch (SocketException e) {
|
||
LogUtil.log(TAG, "UDP socket初始化失败: " + e.getMessage());
|
||
e.printStackTrace();
|
||
} catch (UnknownHostException e) {
|
||
LogUtil.log(TAG, "无法解析目标地址: " + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 初始化UDP流管理器(使用默认端口)
|
||
* @param host 目标主机地址
|
||
*/
|
||
public void init(String host) {
|
||
init(host, DEFAULT_PORT_1_PORT, DEFAULT_FPV_PORT);
|
||
}
|
||
|
||
/**
|
||
* 初始化UDP流管理器(使用默认地址和端口)
|
||
*/
|
||
public void init() {
|
||
init(DEFAULT_HOST, DEFAULT_PORT_1_PORT, DEFAULT_FPV_PORT);
|
||
}
|
||
|
||
/**
|
||
* 初始化视频码流监听器
|
||
*/
|
||
private void initStreamListeners() {
|
||
ICameraStreamManager cameraManager = MediaDataCenter.getInstance().getCameraStreamManager();
|
||
if (cameraManager == null) {
|
||
LogUtil.log(TAG, "CameraStreamManager获取失败");
|
||
return;
|
||
}
|
||
|
||
port1StreamListener=new ICameraStreamManager.ReceiveStreamListener() {
|
||
@Override
|
||
public void onReceiveStream(@NonNull byte[] data, int offset, int length, @NonNull StreamInfo info) {
|
||
if (isStreaming.get()) {
|
||
// 截取有效数据
|
||
byte[] streamData = new byte[length];
|
||
System.arraycopy(data, offset, streamData, 0, length);
|
||
sendStreamData(streamData, ComponentIndexType.PORT_1);
|
||
}
|
||
}
|
||
};
|
||
|
||
// FPV相机码流监听器
|
||
fpvStreamListener=new ICameraStreamManager.ReceiveStreamListener() {
|
||
@Override
|
||
public void onReceiveStream(@NonNull byte[] data, int offset, int length, @NonNull StreamInfo info) {
|
||
if (isStreaming.get()) {
|
||
// 截取有效数据
|
||
byte[] streamData = new byte[length];
|
||
System.arraycopy(data, offset, streamData, 0, length);
|
||
sendStreamData(streamData, ComponentIndexType.FPV);
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
// 注册监听器
|
||
cameraManager.addReceiveStreamListener(ComponentIndexType.PORT_1, port1StreamListener);
|
||
cameraManager.addReceiveStreamListener(ComponentIndexType.FPV, fpvStreamListener);
|
||
|
||
LogUtil.log(TAG, "视频码流监听器注册成功");
|
||
}
|
||
|
||
/**
|
||
* 发送码流数据
|
||
* @param streamData 码流数据
|
||
* @param componentIndex 相机组件索引
|
||
*/
|
||
private void sendStreamData(byte[] streamData, ComponentIndexType componentIndex) {
|
||
if (targetAddress == null) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 选择对应的UDP socket和端口
|
||
DatagramSocket socket = null;
|
||
int port = 0;
|
||
|
||
if (componentIndex == ComponentIndexType.PORT_1) {
|
||
socket = port1UdpSocket;
|
||
port = port1Port;
|
||
} else if (componentIndex == ComponentIndexType.FPV) {
|
||
socket = fpvUdpSocket;
|
||
port = fpvPort;
|
||
}
|
||
|
||
if (socket == null) {
|
||
return;
|
||
}
|
||
|
||
// 构造UDP数据包
|
||
// 前4字节表示相机索引(1: PORT_1, 2: FPV)
|
||
// 接下来4字节表示数据长度
|
||
// 剩余部分为码流数据
|
||
int headerSize = 8;
|
||
byte[] packetData = new byte[headerSize + streamData.length];
|
||
|
||
// 写入相机索引
|
||
int cameraIndex = componentIndex == ComponentIndexType.PORT_1 ? 1 : 2;
|
||
packetData[0] = (byte) (cameraIndex >> 24);
|
||
packetData[1] = (byte) (cameraIndex >> 16);
|
||
packetData[2] = (byte) (cameraIndex >> 8);
|
||
packetData[3] = (byte) cameraIndex;
|
||
|
||
// 写入数据长度
|
||
packetData[4] = (byte) (streamData.length >> 24);
|
||
packetData[5] = (byte) (streamData.length >> 16);
|
||
packetData[6] = (byte) (streamData.length >> 8);
|
||
packetData[7] = (byte) streamData.length;
|
||
|
||
// 写入码流数据
|
||
System.arraycopy(streamData, 0, packetData, headerSize, streamData.length);
|
||
|
||
// 发送UDP数据包
|
||
DatagramPacket packet = new DatagramPacket(
|
||
packetData,
|
||
packetData.length,
|
||
targetAddress,
|
||
port
|
||
);
|
||
|
||
socket.send(packet);
|
||
|
||
} catch (IOException e) {
|
||
LogUtil.log(TAG, "发送UDP数据包失败: " + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 开始UDP流推送
|
||
*/
|
||
public void startStreaming() {
|
||
if (!isInitialized.get()) {
|
||
LogUtil.log(TAG, "UDP流管理器未初始化,请先调用init方法");
|
||
return;
|
||
}
|
||
|
||
isStreaming.set(true);
|
||
LogUtil.log(TAG, "UDP流推送已启动");
|
||
}
|
||
|
||
/**
|
||
* 停止UDP流推送
|
||
*/
|
||
public void stopStreaming() {
|
||
isStreaming.set(false);
|
||
LogUtil.log(TAG, "UDP流推送已停止");
|
||
}
|
||
|
||
/**
|
||
* 关闭UDP流管理器
|
||
*/
|
||
public void close() {
|
||
stopStreaming();
|
||
|
||
// 移除视频码流监听器
|
||
ICameraStreamManager cameraManager = MediaDataCenter.getInstance().getCameraStreamManager();
|
||
if (cameraManager != null) {
|
||
if (port1StreamListener != null) {
|
||
cameraManager.removeReceiveStreamListener(port1StreamListener);
|
||
}
|
||
if (fpvStreamListener != null) {
|
||
cameraManager.removeReceiveStreamListener(fpvStreamListener);
|
||
}
|
||
}
|
||
|
||
// 关闭UDP socket
|
||
if (port1UdpSocket != null) {
|
||
port1UdpSocket.close();
|
||
port1UdpSocket = null;
|
||
}
|
||
if (fpvUdpSocket != null) {
|
||
fpvUdpSocket.close();
|
||
fpvUdpSocket = null;
|
||
}
|
||
|
||
isInitialized.set(false);
|
||
LogUtil.log(TAG, "UDP流管理器已关闭");
|
||
}
|
||
|
||
/**
|
||
* 设置目标地址
|
||
* @param host 目标主机地址
|
||
* @param port1Port PORT_1相机的目标端口
|
||
* @param fpvPort FPV相机的目标端口
|
||
*/
|
||
public void setTargetAddress(String host, int port1Port, int fpvPort) {
|
||
if (host != null) {
|
||
targetHost = host;
|
||
}
|
||
if (port1Port > 0) {
|
||
this.port1Port = port1Port;
|
||
}
|
||
if (fpvPort > 0) {
|
||
this.fpvPort = fpvPort;
|
||
}
|
||
|
||
udpExecutor.execute(() -> {
|
||
try {
|
||
targetAddress = InetAddress.getByName(targetHost);
|
||
LogUtil.log(TAG, "目标地址已更新: " + targetHost + ", PORT_1端口: " + this.port1Port + ", FPV端口: " + this.fpvPort);
|
||
} catch (UnknownHostException e) {
|
||
LogUtil.log(TAG, "无法解析新目标地址: " + e.getMessage());
|
||
e.printStackTrace();
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取UDP流推送状态
|
||
*/
|
||
public boolean isStreaming() {
|
||
return isStreaming.get();
|
||
}
|
||
|
||
/**
|
||
* 获取UDP流管理器初始化状态
|
||
*/
|
||
public boolean isInitialized() {
|
||
return isInitialized.get();
|
||
}
|
||
|
||
/**
|
||
* 获取当前目标地址
|
||
*/
|
||
public String getTargetAddress() {
|
||
return targetHost + ", PORT_1端口: " + port1Port + ", FPV端口: " + fpvPort;
|
||
}
|
||
} |