接收端:
public class ReceiveThread {
private static final String TAG = "ReceiveThread";
private final Handler mHandler;
private final SurfaceHolder mHolder;
private final String mIp;
private final Activity mContext;
private DatagramSocket mSocket;
private Thread thread;
/**
* @description 图片数据接收完全的标识
*/
public final static String PACKET_END = "#stream_end#";
/**
* @description
*/
public ReceiveThread(Activity context,Handler handler, SurfaceHolder holder, String ip) {
this.mHandler = handler;
this.mHolder = holder;
this.mIp = ip;
this.mContext = context;
}
public void startReceiveThread() {
thread = new Thread(new Runnable() {
@Override
public void run() {
// 错误信息,默认为"错误"
String errMsg = "错误";
// 用于接收图像数据的字节数组输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
// 结束标记的长度(结合日期时间)
int endlen = (PACKET_END + getLocalDateTime()).getBytes().length;
if (mSocket == null) {
// 创建一个无绑定状态的DatagramSocket(UDP套接字)
mSocket = new DatagramSocket(null);
// 允许地址重用 不用这个会很卡 因为一次用完不是直接清空的 会等待一会才清空socket
mSocket.setReuseAddress(true);
// 绑定本地地址和端口号 监听这个ip和端口的数据
mSocket.bind(new InetSocketAddress(Constants.FIND_CAMERA_PORT));
// 设置套接字阻塞超时时间为3秒
mSocket.setSoTimeout(3000);
MlogUtil.e(TAG, "发送请求相机连接..... " +endlen);
// 通过套接字发送请求,并将接收到的数据写入字节数组输出流baos中
sendRet(mSocket, baos);
// 重置字节数组输出流
baos.reset();
MlogUtil.e(TAG, "等待接收... "+endlen);
// 拿数据 循环接收数据,直到套接字为空
while (mSocket != null) {
// 接收数据的缓冲区
byte[] buffer = new byte[1024];
// 创建用于接收数据的数据报包
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
// 接收数据 // 通过套接字接收数据,并将接收到的数据填充到数据报包中
mSocket.receive(packet);
} catch (SocketTimeoutException te) {
te.printStackTrace();
MlogUtil.e(TAG, "连接超时... "+endlen);
// 再次发送请求
sendRet(mSocket, baos);
baos.reset();
// 重新接收
continue;
}
// 判断一帧图片是否接收完成
if (packet.getLength() == endlen) {
// 获取接收到的数据并转换为字符串 这个数据是获取时间
String end = new String(packet.getData(), 0, endlen);
// 判断结束标记是否匹配
if (end.startsWith(PACKET_END)) {
// 拆分结束标记
String[] args = end.split("#");
String time = "--:--";
if (args.length == 3) {
time = args[2]; // 获取时间信息
}
updateViewDisplay(baos, time); // 更新图像视图的显示
Thread.sleep(150); // 等待一段时间,准备接收下一帧图像数据
baos.flush(); // 刷新字节数组输出流
// 再次发送请求
sendRet(mSocket, baos);
baos.reset(); // 重置字节数组输出流
MlogUtil.e(TAG, "发送请求相机连接..... " + getLocalDateTime());
continue;
}
}
baos.write(packet.getData(), 0, packet.getLength()); // 将接收到的数据写入字节数组输出流
}
}
} catch (SocketException e) {
e.printStackTrace();
errMsg = e.getMessage(); // 获取Socket异常的错误信息
} catch (IOException e) {
e.printStackTrace();
errMsg = e.getMessage(); // 获取IO异常的错误信息
} catch (InterruptedException e) {
e.printStackTrace();
errMsg = e.getMessage(); // 获取中断异常的错误信息
} catch (Exception e) {
e.printStackTrace();
errMsg = e.getMessage(); // 获取其他异常的错误信息
} finally {
try {
baos.close(); // 关闭字节数组输出流
} catch (IOException e) {
e.printStackTrace();
}
cancelThread(errMsg); // 取消线程并传递错误信息
}
}
});
thread.start(); // 启动线程开始接收图像数据
}
public void cancelThread(String errMsg) {
if(mSocket != null) {
if(mSocket.isConnected()) mSocket.disconnect();
if(!mSocket.isClosed()) this.mSocket.close();
mSocket = null;
}
if(thread != null) {
thread.interrupt();
thread = null;
}
if (errMsg!=null && errMsg.isEmpty()!=true) {
MlogUtil.i(">>>BaseThread", "cancelThread "+ errMsg);
}
}
/**
* @description 接收图像后 显示图像
*/
private void updateViewDisplay(final ByteArrayOutputStream baos, final String time) {
byte[] data = baos.toByteArray();
//把数据转成bitmap
final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
if(bitmap == null) {
Log.i(TAG, "数据为空.....");
return;
}
mContext.runOnUiThread(new Runnable() {
@Override
public void run() {
Canvas canvas = mHolder.lockCanvas(null);
if (canvas == null) {
return;
}
Bitmap bitmap2 = bitmap.createScaledBitmap(bitmap, canvas.getWidth(), canvas.getHeight(), true);
/* bitmap2= rotateBitmap(bitmap2, 90);
canvas.drawBitmap(bitmap2, (canvas.getWidth()-canvas.getHeight())/2, 0, null);*/
canvas.drawBitmap(bitmap2, 0, 0, null);
Paint p = new Paint();
p.setTextSize(24);
p.setColor(Color.WHITE);
/*canvas.drawText(time, 20, 30, p);*/
mHolder.unlockCanvasAndPost(canvas);
// baos.flush();
baos.reset();
}
});
}
/**
* @description 发送数据
*/
private void sendRet(DatagramSocket dSocket, ByteArrayOutputStream baos) throws IOException, Exception {
if (dSocket==null) {
throw new Exception("socket is null");
}
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(new Integer(Constants.RET_CAMERA_IP));
byte[] data = baos.toByteArray();
InetAddress address = InetAddress.getByName(mIp);
DatagramPacket pack = new DatagramPacket(data, data.length, address, Constants.FIND_CAMERA_PORT);
dSocket.send(pack);
oos.flush();
baos.flush();
}
/**
* @description 获取当前本地时间
*/
public String getLocalDateTime() {
return (new Date()).toLocaleString();
}
}
发送端:
public class SenderThread {
private static final String TAG = "SenderThread"; // 日志标签
private final Activity mContext; // 上下文对象
private int cameraCount = 0; // 摄像头数量
public boolean isSending = false; // 是否正在发送图像
private DeviceInfo info; // 设备信息
private YuvImage image = null; // 图像对象
private Thread thread; // 线程对象
private DatagramSocket mSocket; // 数据报套接字
public SenderThread(Activity context, DeviceInfo info, Handler handler) {
this.info = info;
this.mContext = context;
}
public String getDeviceName() {
return info.getName(); // 获取设备名称
}
public String getLocalIp() {
return info.getLocalIp(); // 获取本地IP地址
}
public int getCameraCount() {
return cameraCount; // 获取摄像头数量
}
public void setCameraCount(int cameraCount) {
this.cameraCount = cameraCount; // 设置摄像头数量
}
public void canFind(boolean isFind) {
if (isFind == true && thread == null) {
this.thread = new Thread(new Runnable() {
@Override
public void run() {
String errMsg = "未知错误";
try {
mSocket = new DatagramSocket(null);
mSocket.setReuseAddress(true);
// 监听本地端口
mSocket.bind(new InetSocketAddress(Constants.FIND_CAMERA_PORT));
do {
MlogUtil.d(TAG, "等待中... ");
DatagramPacket pack = new DatagramPacket(new byte[1028], 1028);
try {
mSocket.receive(pack); // 接收数据报包
} catch (SocketTimeoutException e) {
e.printStackTrace();
continue;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
SocketAddress sendAddress = pack.getSocketAddress(); // 获取发送方的地址
ObjectInputStream ois = null;
Integer code = 0;
try {
ois = new ObjectInputStream(new ByteArrayInputStream(pack.getData()));
code = (Integer) ois.readObject(); // 从数据包中读取操作码
} catch (StreamCorruptedException e) {
e.printStackTrace();
MlogUtil.e(TAG, "连接被断开 " + e);
} catch (Exception e) {
e.printStackTrace();
MlogUtil.e(TAG, "连接被断开 " + e);
} finally {
if (ois != null) {
try {
ois.close();
} catch (Exception ex) {
throw ex;
}
}
}
if (mSocket == null) {
// 如果mSocket为null,跳出循环
break;
}
switch (code) {
case Constants.RET_CAMERA_IP: // 操作码为RET_CAMERA_IP
{
isSending = true;
String sendTime = getLocalDateTime(); // 获取当前本地时间
sendImage(image, sendAddress, sendTime); // 发送图像
MlogUtil.e(TAG, "等待中...已发送 ");
isSending = false;
}
break;
default:
}
} while (!thread.isInterrupted() && mSocket != null);
} catch (Exception e) {
e.printStackTrace();
errMsg = e.getMessage();
} finally {
cancelThread(errMsg); // 取消线程
}
}
});
this.thread.start(); // 启动线程
} else {
cancelThread(""); // 取消线程
}
}
public void cancelThread(String errMsg) {
if (mSocket != null) {
if (mSocket.isConnected()) mSocket.disconnect();
if (!mSocket.isClosed()) this.mSocket.close();
mSocket = null;
}
if (thread != null) {
thread.interrupt();
thread = null;
}
if (errMsg != null && errMsg.isEmpty() != true) {
MlogUtil.i(">>>BaseThread", "cancelThread " + errMsg);
}
}
/**
* @description 获取当前本地时间
*/
public String getLocalDateTime() {
return (new Date()).toLocaleString(); // 返回当前本地时间的字符串表示
}
private synchronized void sendImage(YuvImage image, SocketAddress sendAddress, String sendTime) throws Exception {
if (mSocket == null) {
throw new异常("Socket为空"); // 抛出异常,表示套接字为空
}
if (image == null) {
throw new Exception("获取图片失败"); // 抛出异常,表示获取图像失败
}
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
try {
image.compressToJpeg(new Rect(0, 0, image.getWidth(), image.getHeight()), 80, outStream); // 将图像压缩为JPEG格式并写入输出流
byte[] buffer = new byte[1024];
ByteArrayInputStream bais = new ByteArrayInputStream(outStream.toByteArray());
outStream.flush();
outStream.close();
try {
int len;
DatagramPacket pack;
while ((len = bais.read(buffer, 0, buffer.length)) != -1) {
pack = new DatagramPacket(buffer, len, sendAddress); // 创建数据报包,并指定发送方的地址
mSocket.send(pack); // 发送数据报包
}
byte[] end = (PACKET_END + sendTime).getBytes(); // 组装结束标志和发送时间的字节数组
pack = new DatagramPacket(end, end.length, sendAddress); // 创建包含结束标志和发送时间的数据报包
mSocket.send(pack); // 发送数据报包
} catch (Exception e) {
e.printStackTrace();
} finally {
bais.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
outStream.close();
image = null;
}
}
public void setSendCameraImage(YuvImage image) {
if (isSending()) {
return;
}
this.image = image; // 设置要发送的图像
}
public boolean isSending() {
return isSending; // 返回是否正在发送图像
}
}