干货 | Unity中基于RPC机制实现摄像头视频传输

发表于2015-07-20
评论1 3.7k浏览

Unity中的RPC机制不能直接将视频进行传输,所以要进行视频帧图片到字节的转换,在将字节流进行传输。

首先,客户端的代码如下

[csharp] view plaincopy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class Client : MonoBehaviour {  
  5.     //要连接的服务器地址  
  6.     //string IP = "127.0.0.1";//本地电脑地址  
  7.     string IP = "192.168.1.100";  
  8.     public  Texture2D m_recevieTex;  
  9.    // string IP = "192.168.1.102";  
  10.     //要连接的端口  
  11.     int Port = 10100;  
  12.     //聊天信息  
  13.     string Message = "";  
  14.     //声明一个二维向量   
  15.     Vector2 Sc;  
  16.     public UITexture m_uiCameraPlane;     
  17.     void OnGUI(){  
  18.         //端类型的状态  
  19.         switch(Network.peerType){  
  20.             //禁止客户端连接运行, 服务器未初始化  
  21.             case NetworkPeerType.Disconnected:  
  22.                 StartConnect();  
  23.                 break;  
  24.             //运行于服务器端  
  25.             case NetworkPeerType.Server:  
  26.                 break;  
  27.             //运行于客户端  
  28.             case NetworkPeerType.Client:  
  29.                 OnClient();  
  30.                 break;  
  31.             //正在尝试连接到服务器  
  32.             case NetworkPeerType.Connecting:  
  33.                 break;  
  34.         }  
  35.     }  
  36.           
  37.     void StartConnect(){  
  38.         if (GUILayout.Button("连接服务器")){  
  39.             NetworkConnectinoxss error = Network.Connect(IP,Port);  
  40.             //连接状态  
  41.             switch(error){  
  42.                 case NetworkConnectinoxss.NoError:  
  43.                     break;  
  44.                 default:  
  45.                     Debug.Log("客户端错误"+error);  
  46.                     break;  
  47.             }  
  48.         }  
  49.     }  
  50.       
  51.     void OnClient(){  
  52.         //创建开始滚动视图  
  53.         Sc = GUILayout.BeginScrollView(Sc,GUILayout.Width(280),GUILayout.Height(400));  
  54.         //绘制纹理, 显示内容  
  55.         GUILayout.Box(Message);  
  56.         //文本框  
  57.         Message = GUILayout.TextArea(Message);  
  58.         if (GUILayout.Button("发送")){  
  59.             //发送给接收的函数, 模式为全部, 参数为信息  
  60.             //networkView.RPC("ReciveMessage", RPCMode.All, Message);  
  61.             networkView.RPC("ReciveCameraTex", RPCMode.All, m_recevieTex.EncodeToJPG());  
  62.         }  
  63.         //结束滚动视图, 注意, 与开始滚动视图成对出现  
  64.         GUILayout.EndScrollView();  
  65.           
  66.     }  
  67.       
  68.     //接收请求的方法. 注意要在上面添加[RPC]  
  69.     [RPC]  
  70.     void ReciveMessage(string msg, NetworkMessageInfo info){  
  71.         //刚从网络接收的数据的相关信息,会被保存到NetworkMessageInfo这个结构中  
  72.         Message = "发送端"+info.sender  +"消息"+msg;  
  73.     }  
  74.     [RPC]  
  75.     void ReciveCameraTex(byte[] camTex, NetworkMessageInfo info)  
  76.     {  
  77.         m_recevieTex=new Texture2D(Screen.width, Screen.height);  
  78.         m_recevieTex.LoadImage(camTex);  
  79.         m_uiCameraPlane.mainTexture = m_recevieTex;  
  80.     }  
  81.     // Use this for initialization  
  82.     void Start () {  
  83.       
  84.     }  
  85.       
  86.     // Update is called once per frame  
  87.     void Update () {  
  88.       
  89.     }  
  90. }  
客户端主要是用来接收的,所以比较简单。在接收函数ReciveCameraTex中直接进行转换就可以了。

相对的服务端就比较复杂一点,代码如下

[csharp] view plaincopy
  1. using UnityEngine;  
  2. using System.Collections;  
  3. public class Severs : MonoBehaviour {  
  4.     public UITexture m_cameraShowTex;  
  5.     public Texture2D m_sendTex;  
  6.     //public Texture2D m_sendTexText;  
  7.     public byte[] m_tempBytes;  
  8.     private RenderTexture m_currentRT;  
  9.     private RenderTexture m_renderTex;  
  10.     int Port = 10100;  
  11.     string Message = "";  
  12.     string m_cameraName;  
  13.     //声明一个二维向量   
  14.     Vector2 Sc;  
  15.     WebCamTexture m_cameraTex;  
  16.     float m_zoomRate;//焦距  
  17.     bool m_isPlay=false;  
  18.     bool m_isSend = false;  
  19.     // Use this for initialization  
  20.     void Start()  
  21.     {  
  22.         m_zoomRate = 1;  
  23.         m_renderTex = new RenderTexture(300, 300, 24, RenderTextureFormat.ARGB32);  
  24.        // m_renderTex = new RenderTexture(Screen.width,Screen.height, 24, RenderTextureFormat.ARGB32);  
  25.         m_sendTex = new Texture2D(m_renderTex.width,m_renderTex.height);  
  26.         Camera.main.targetTexture = m_renderTex;  
  27.         StartCoroutine(openCamera(0));  
  28.     }  
  29.   
  30.     // Update is called once per frame  
  31.     void Update()  
  32.     {  
  33.     }  
  34.     IEnumerator openCamera(int whichOne)  
  35.     {  
  36.         yield return Application.RequestUserAuthorization(UserAuthorization.WebCam);  
  37.         if (Application.HasUserAuthorization(UserAuthorization.WebCam))  
  38.         {  
  39.             //可以通过下面的这个数组的索引来选择调用手机上的前置或者后置摄像头,另外这个可以自动对焦  
  40.             WebCamDevice[] devices = WebCamTexture.devices;  
  41.             if (devices.Length <= whichOne)  
  42.             {  
  43.                 m_cameraName = devices[0].name;  
  44.             }  
  45.             else  
  46.             {  
  47.                 m_cameraName = devices[whichOne].name;  
  48.                 if (whichOne > 0)  
  49.                 {  
  50.                    /* Quaternion temp = m_uiCameraPlane.transform.localRotation; 
  51.                     temp.eulerAngles = new Vector3(0, 0, 90); 
  52.                     m_uiCameraPlane.transform.localRotation = temp;*/  
  53.                 }  
  54.             }  
  55.             m_cameraTex = new WebCamTexture(m_cameraName, Screen.width, Screen.height, 30);  
  56.             m_cameraTex.anisoLevel = 9;  
  57.             float heightRate = Screen.height / m_cameraTex.height;  
  58.             float widthRate = Screen.width / m_cameraTex.width;  
  59.             //Debug.Log(heightRate.ToString());  
  60.             //Debug.Log(widthRate.ToString());  
  61.             m_zoomRate = heightRate > widthRate ? heightRate : widthRate;  
  62.             if (m_zoomRate > 2)  
  63.             {  
  64.                 m_zoomRate = 2;  
  65.             }  
  66.             m_cameraTex.Play();  
  67.             m_isPlay = true;  
  68.         }  
  69.     }    
  70.     //OnGUI方法,所有GUI的绘制都需要在这个方法中实现  
  71.     void OnGUI(){  
  72.   
  73.         //Network.peerType是端类型的状态:  
  74.         //即disconnected, connecting, server 或 client四种  
  75.         switch(Network.peerType){  
  76.             //禁止客户端连接运行, 服务器未初始化  
  77.             case NetworkPeerType.Disconnected:  
  78.                 StartServer();  
  79.                 break;  
  80.             //运行于服务器端  
  81.             case NetworkPeerType.Server:  
  82.                 OnServer();  
  83.                 break;  
  84.             //运行于客户端  
  85.             case NetworkPeerType.Client:  
  86.                 break;  
  87.             //正在尝试连接到服务器  
  88.             case NetworkPeerType.Connecting:  
  89.                 break;  
  90.         }  
  91.   
  92.         if (m_isPlay)  
  93.         {  
  94.   
  95.             m_cameraShowTex.mainTexture = m_cameraTex;  
  96.             //NGUIDebug.Log(" cameraTexture.height:" + cameraTexture.height.ToString() + "cameraTexture.width:" + cameraTexture.width.ToString());  
  97.             m_cameraShowTex.width= m_cameraTex.width;  
  98.             m_cameraShowTex.height = m_cameraTex.height;  
  99.   
  100.            // m_sendTex = RTImage(Camera.main);   
  101.            // m_tempBytes = m_sendTex.EncodeToPNG();  
  102.             if (m_isSend)  
  103.             {  
  104.                 StartCoroutine(changeSendTexToBytes());  
  105.             }  
  106.         }  
  107.     }  
  108.     IEnumerator changeSendTexToBytes()  
  109.     {  
  110.         m_isPlay = false;  
  111.         RTImage();  
  112.         m_tempBytes = m_sendTex.EncodeToPNG();  
  113.         yield return new WaitForSeconds(0.35f);  
  114.         networkView.RPC("ReciveCameraTex", RPCMode.All, m_tempBytes);  
  115.         m_isPlay = true;  
  116.     }  
  117.     void RTImage()  
  118.     {  
  119.         m_currentRT = RenderTexture.active;  
  120.         RenderTexture.active = m_renderTex;  
  121.         Camera.main.Render();  
  122.         m_sendTex.ReadPixels(new Rect(0, 0, m_cameraShowTex.width, m_cameraShowTex.height), 0, 0);  
  123.         m_sendTex.Apply();  
  124.         RenderTexture.active = m_currentRT;  
  125.           
  126.     }  
  127.     Texture2D RTImage(Camera cam)  
  128.     {  
  129.         RenderTexture currentRT = RenderTexture.active;  
  130.         RenderTexture.active = cam.targetTexture;  
  131.         cam.Render();  
  132.         Texture2D image = new Texture2D(cam.targetTexture.width, cam.targetTexture.height);  
  133.         image.ReadPixels(new Rect(0, 0, cam.targetTexture.width, cam.targetTexture.height), 0, 0);  
  134.         image.Apply();  
  135.         RenderTexture.active = currentRT;  
  136.         return image;  
  137.     }  
  138.     void StartServer(){  
  139.         //当用户点击按钮的时候为true  
  140.         if ( GUI.Button(new Rect(10, 10, 100, 20),"创建服务器")) {  
  141.             //初始化本机服务器端口,第一个参数就是本机接收多少连接  
  142.             NetworkConnectinoxss error = Network.InitializeServer(12,Port,false);  
  143.             //连接状态  
  144.             switch(error){  
  145.                 case NetworkConnectinoxss.NoError:  
  146.                     break;  
  147.                 default:  
  148.                     Debug.Log("服务端错误"+error);  
  149.                     break;  
  150.             }  
  151.         }  
  152.     }  
  153.       
  154.     void OnServer(){  
  155.          GUI.Label(new Rect(10, 30, 100, 20),"服务端已经运行,等待客户端连接");  
  156.         //Network.connections是所有连接的玩家, 数组[]  
  157.         //取客户端连接数.   
  158.         int length = Network.connections.Length;  
  159.         //按数组下标输出每个客户端的IP,Port  
  160.         for (int i=0; i<length; i++)  
  161.         {  
  162.             GUI.Label(new Rect(10, 50, 300, 20),"客户端" + i);  
  163.             GUI.Label(new Rect(10, 70, 300, 20),"客户端ip"+Network.connections[i].ipAddress);  
  164.             GUI.Label(new Rect(10, 90, 300, 20),"客户端端口"+Network.connections[i].port);  
  165.              GUI.Label(new Rect(10, 100, 300, 20),"-------------------------------");  
  166.         }  
  167.         //当用户点击按钮的时候为true  
  168.         if (GUI.Button(new Rect(10, 120,300, 20),"断开服务器")){  
  169.             Network.Disconnect();  
  170.         }  
  171.         if(GUI.Button(new Rect(500, 120,300, 20),"发送"))  
  172.         {  
  173.             m_isSend = true;  
  174.       
  175.         }  
  176.     }  
  177.     //接收请求的方法. 注意要在上面添加[RPC]  
  178.     [RPC]  
  179.     void ReciveCameraTex(byte[] camTex, NetworkMessageInfo info)  
  180.     {  
  181.         //m_sendTexText.LoadImage(m_tempBytes);  
  182.     }  
  183.       
  184. }  
因为Unity中获取得到的摄像头视频,不能直接转换成字节必须要转换成Textture2D之后在使用EncodeToPNG()函数转换成字节,而将摄像头的WebCamTexture也不能直接转换成Texture2D,在官网上提供了这样一个转换方法
[csharp] view plaincopy
  1. Texture2D RTImage(Camera cam)  
  2.   {  
  3.       RenderTexture currentRT = RenderTexture.active;  
  4.       RenderTexture.active = cam.targetTexture;  
  5.       cam.Render();  
  6.       Texture2D image = new Texture2D(cam.targetTexture.width, cam.targetTexture.height);  
  7.       image.ReadPixels(new Rect(0, 0, cam.targetTexture.width, cam.targetTexture.height), 0, 0);  
  8.       image.Apply();  
  9.       RenderTexture.active = currentRT;  
  10.       return image;  
  11.   }  

好了大功告成,我写的比较粗糙,没有做缓存处理和关键帧的计算,主要集中在怎么转换摄像头的WebCamTexture到字节上,要有一定写UnityRPC基础的童鞋才可以看得懂并进行调试我的代码,在两个电脑上比较流畅,在手机上还是比较卡。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引