使用Socket发送安卓端摄像机的内容其实也就是相当于发送一张图片,不同的是,这里是调用安卓的摄像机然后在进行实时发送。

发送流程是:点对点的发送,

发送方:

  1. 捕获摄像机的数据
  2. 将数据显示在纹理上
  3. 将数据编程字节流
  4. 将字节流发送出去

接收方

  1. 接收字节流
  2. 切换到主线程
  3. 将字节流转换为图片
  4. 把图片显示出来

首先创建两个RawImage,用来当做显示面板,一个用来做发送端一个用来做接收端。
在这里插入图片描述

这次Socket采用UDP协议来进行传输。先进行封装一个ScoketUdp的类,后面用来调用发送和接收消息。

流程就是那几步,创建Socket、绑定IP和端口号、接收和发送消息、断开链接。’

这里直接放代码:

using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
using System.Net;
using System.Threading;
public class UdpSocket
{
    Socket myUdpScoket;
    byte[] recvBuffer;//发送的容器
    IPEndPoint recvPoint = new IPEndPoint(IPAddress.Any, 0);
    bool isRunning = true;
    Thread t;
    /// <summary>
    /// 接收到消息的回调函数 委托
    /// </summary>
    public delegate void RecvDelegate(byte[] buffers, int realSize, string ip, int port);

    RecvDelegate recvCallBack;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="port"></param>
    /// <param name="bufferSize"></param>
    public UdpSocket(int port,int bufferSize,RecvDelegate recvDelegate)
    {
        recvCallBack = recvDelegate;
        Init(port,bufferSize);
    }

    public void Init(int port, int bufferSize)
    {
        recvBuffer = new byte[bufferSize];//接收消息的容器
        myUdpScoket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        //IPAddress 绑定网卡 IPAddress.Any自动获取一个闲的网卡
        //10080为端口号
        IPEndPoint tmpIPPoint = new IPEndPoint(IPAddress.Any, port);
        //绑定端口号和IP地址
        myUdpScoket.Bind(tmpIPPoint);
        t = new Thread(ReceiveThrea);
        t.Start();
    }
    /// <summary>
    /// 发送消息
    /// </summary>
    public void SendMessage(byte[] buffers,string ip,int port)
    {
        //buffers 所需要发送的字节流  
        //buffers.Length发送的长度
        //SocketFlags.None标记
        //new IPEndPoint(IPAddress.Parse(ip), port) 发送的ip地址和端口号
        //realSend 真正发送的字节数量
        int realSend = myUdpScoket.SendTo(buffers, buffers.Length, SocketFlags.None, new IPEndPoint(IPAddress.Parse(ip), port));
        
    }

    /// <summary>
    /// 接受消息的方法
    /// </summary>
    public void ReceiveMessage()
    {
        EndPoint tmpPoint = recvPoint;
        int realRecv = myUdpScoket.ReceiveFrom(recvBuffer, ref tmpPoint);
        ///调用回调函数
        recvCallBack(recvBuffer, realRecv, recvPoint.Address.ToString(), recvPoint.Port);
    }
  
    /// <summary>
    /// 异步接收
    /// </summary>
    public void ReceiveThrea()
    {       
        while (isRunning)
        {
            if (myUdpScoket != null && myUdpScoket.Available < 1)
            {
                Thread.Sleep(100);
            }
            ReceiveMessage();
        }
    }
    /// <summary>
    /// 断开链接的方法
    /// </summary>
    public void Dispose()
    {
        isRunning = false;
        t.Abort();
        if (myUdpScoket != null)
        {
            myUdpScoket.Close();
        }
    }
}

然后我们按照上方的流程来一点一点的做:

首先发送方,需要捕获摄像机的内容,在Unity中有WebCamDevice这个API可以供我们使用
创建一个脚本,挂载到发送方,在Start方法中来来捕获摄像机的内容,并把它显示出来。
在这里插入图片描述将图片转换成字节流,在Texture中有一个EncodeToJPG方法来供我们实现。每张图片都是一个二维数组的像素点,数组中的元素就是图片宽和高的位置信息。得到图片的大小以后进行二次循环,进行设置像素点即可。

在这里插入图片描述创建一个发送的方法,使用上面我们封装好的Socket进行发送消息。

创建一个Socket对象,封装一个发送方法。
在这里插入图片描述在获取摄像机内容的脚本中进行发送

在这里插入图片描述

这样发送方完成。

在创建一个用来接收的脚本。

先创建一个Socket,并创建出来,端口号为18002,因为在发送消息的时候,发送的端口号为18002,所以接收时的端口号一定为18002。

在上面我们封装好的Socket中,接收到消息时,我们会调用一个回调方法,也就是RecvBuffer这个方法。

定一个字节数组,把收到字节使用lock方式拷贝到这个字节数组中,完成辅线程到主线程的转换。

把字节流转换到图片,在Texture2d中有LoadImage这个API供我们使用,只需要传递一个字节数组就可以了,所以在Start方法中,还需要定义一个Texture2D,并赋值给接收方的RawImage的texture。

具体代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ReceiveImage : MonoBehaviour
{
    UdpSocket udpSocket;

    byte[] receBuffer;
    bool isRecv;
    Texture2D texture2;
    private void Start()
    {
        udpSocket = new UdpSocket(18002, 1024 * 1024 * 10, new UdpSocket.RecvDelegate(RecvBuffer));

         texture2 = new Texture2D(600,600);
         transform.GetComponent<RawImage>().texture = texture2;

    }


    private void Update()
    {
        if (isRecv)
        {
            isRecv = false;

            texture2.LoadImage(receBuffer);
        }
    }

    void RecvBuffer(byte[] buffers, int realSize, string ip, int port)
    {
        lock (this)
        {
            receBuffer = new byte[realSize];

            Buffer.BlockCopy(buffers,0,receBuffer,0,realSize);
        }
        isRecv = true;
    }

}

全部完成,打包安卓包,在手机运行,点击Button按钮就可以完成发送,记得给button添加点击事件,点击事件为获取摄像机方法中OnButtonClick()事件。

本人正在学习中,如果有错误欢迎各位大佬提携提携小弟,感激不尽!!!

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐