5

正如问题所述,我一直在尝试将 WebCamTexture 从带有网络摄像头的客户端流式传输到服务器。双方(客户端和服务器)都在 Unity 中。稍后,客户端将部署到 Android,服务器将成为桌面应用程序。

目前我正在使用以下方法获取纹理的像素:

tex.GetPixels32();

并使用自定义序列化程序对它们进行序列化(以优化其大小)。我目前有一个未压缩的字节数组,每帧大约 3.5MB 准备发送。我知道它很大,但我想在压缩部分和实时部分开始之前传输它。

该过程的最后一部分应该是使用新的 Unity NetworkTransport 静态类发送它。距离上次使用sockets已经很久了,我真的很烂。目前我无法让它工作。

这是我的服务器端代码(为了清楚起见,省略序列化代码):

void Start()
{
    webcamTexture = new WebCamTexture();
    Background.texture = webcamTexture;
    Background.material.mainTexture = webcamTexture;
    webcamTexture.Play();

    if (!_isStarted)
    {
        _isStarted = true;

        NetworkTransport.Init();
        m_Config = new ConnectionConfig();
        m_CommunicationChannel  = m_Config.AddChannel(QosType.ReliableFragmented);
        HostTopology topology = new HostTopology(m_Config, 12);
        m_GenericHostId = NetworkTransport.AddHost(topology, 0);
        byte error;
        m_ConnectionId = NetworkTransport.Connect(m_GenericHostId, ip, port, 0, out error);
    }
}

void Update()
{
    if (!_isStarted)
        return;

    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

    switch (recData)
    {
        case NetworkEventType.Nothing:         //1
            break;
        case NetworkEventType.ConnectEvent:    //2
            Debug.Log("Received connection confirmation");
            _readyToSend = true;
            break;
        case NetworkEventType.DataEvent:       //3

            break;
        case NetworkEventType.DisconnectEvent: //4
            //one of the established connection has been disconnected
            Debug.Log(String.Format("Disconnect from host {0} connection {1}", recHostId, connectionId));

            break;
    }

    if (_readyToSend)
    {
        _readyToSend = false; // To send just the first frame

        byte[] colourArray = SerializeObject(MakeSerializable(GetRenderTexturePixels(webcamTexture))); // Serialize the webcam texture

        // Sending total size
        byte[] sizeToSend = BitConverter.GetBytes(colourArray.Length);
        NetworkTransport.Send(m_GenericHostId, m_ConnectionId, m_CommunicationChannel, sizeToSend, sizeToSend.Length, out error);

        byte[] bytes = new byte[bufferLenght];
        int remainingBytes = colourArray.Length;
        int index = 0;
        int i = 1;

        while (remainingBytes >= bufferLenght)
        {
            System.Buffer.BlockCopy(colourArray, index, bytes, 0, bufferLenght);
            NetworkTransport.Send(m_GenericHostId, m_ConnectionId, m_CommunicationChannel, bytes, bytes.Length, out error);
            remainingBytes -= bufferLenght;
            Debug.Log(i++ + "Remaining bytes: " + remainingBytes + " - Error: "+error);
            index += bufferLenght;
        }

        if (remainingBytes > 0) // Send the last fragment below bufferLenght bytes
        {
            System.Buffer.BlockCopy(colourArray, index, bytes, 0, remainingBytes);
            NetworkTransport.Send(m_GenericHostId, m_ConnectionId, m_CommunicationChannel, bytes, remainingBytes, out error);
            Debug.Log("Error: "+error);
        }
    }
}

这是客户端:

void Start()
{
    if (!_isStarted)
    {
        _isStarted = true;

        NetworkTransport.Init();

        m_Config = new ConnectionConfig();

        m_CommunicationChannel  = m_Config.AddChannel(QosType.ReliableFragmented);

        HostTopology topology = new HostTopology(m_Config, 12);

        m_GenericHostId = NetworkTransport.AddHost(topology, port, null);
    }
}

void Update()
{
    if (!_isStarted)
        return;

    int recHostId; 
    int connectionId; 
    int channelId; 
    byte[] recBuffer = new byte[bufferLenght]; 
    int bufferSize = bufferLenght;
    int dataSize;
    byte error;

    NetworkEventType recData = NetworkTransport.Receive(out recHostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);

    switch (recData)
    {
        case NetworkEventType.Nothing:         //1
            break;

        case NetworkEventType.ConnectEvent:    //2

                //somebody else connect to me
            Log.text += string.Format("Connect from host {0} connection {1}\n", recHostId, connectionId);
            break;

        case NetworkEventType.DataEvent:       //3

            if (!sizeReceived)
            {
                sizeReceived = true;

                if (dataSize == 2)
                {
                    bytesToReceive = BitConverter.ToInt16(recBuffer, 0);
                }
                else if (dataSize == 4)
                {
                    bytesToReceive = BitConverter.ToInt32(recBuffer, 0);
                }

                Debug.Log("We will receive: "+bytesToReceive);
            }
            else
            {
                Log.text = string.Format("Received event host {0} connection {1} channel {2} message length {3}\n", recHostId, connectionId, channelId, dataSize);

                Log.text += "Received " + bufferSize + " bytes\n";
                bytesToReceive -= bufferSize;
                Log.text += "Remaining " + bytesToReceive + " bytes\n";
            }
            break;

        case NetworkEventType.DisconnectEvent: //4

            break;

    }
}

我知道它会在发送之前阻止更新功能,但现在对我来说并不重要,因为我只是想传输一个帧以了解这个新系统是如何工作的,然后从那里继续。目前我在发送第一个包后收到此错误,缓冲区为 32768 字节:

no free events for long message
UnityEngine.Networking.NetworkTransport:Send(Int32, Int32, Int32, Byte[], Int32, Byte&)
CameraStreamer:Update() (at Assets/Scripts/Client/CameraStreamer.cs:112)

我也尝试过使用1024缓冲区,显示消息只需要更长的时间(成功发送100多个包后)。

根据这个线程,它与发送过快的消息和填满队列有关,但没有一个建议的解决方案对我有用。我将不胜感激任何帮助或指导,因为 Unity 文档真的很差。

4

2 回答 2

1

我终于设法通过网络发送了相机截图。似乎尝试发送几条消息而不等待服务器回答填充队列。

我需要做的就是在收到每条消息后用一个字节将答案从服务器发送回客户端。客户端正在等待这种 ACK 发送下一条消息。有了这个,一切都开始像魅力一样工作。

我认为必须有另一种方法来解决这个问题,因为我认为为收到的每条消息都发回一条消息不是很合理,但它现在起到了作用。我将使用其他发现来编辑此答案。

问候。

编辑:原来是一个 Unity 错误,它在 Unity 5.2 中得到解决。(如果我没记错的话)。

于 2015-07-08T08:52:58.743 回答
1

即使使用 Unity 5.2 补丁,这个特定的测试代码/示例仍然会出现问题。

在 Unity 5.3.4f1 中运行代码,我能够看到错误 4 (NetworkError.NoResource) 在大约 200 个数据包之后发生,并且在不久之后停止发送。我认为原因是因为它是阻塞发送,因此消息队列永远不会正确刷新。

我已经重写了代码以在 2 秒延迟后从纹理中抓取网络摄像头图像(因为网络摄像头需要时间来初始化,否则您会发送空白图像)。

之后通过网络摄像头图像发送,现在每次执行更新一个数据包似乎很好。可能是因为更新有时间运行并且没有被阻止。所以发送一个数据包并退出Update函数,将索引和其他变量移出Update。

希望这对其他人有帮助,我花了 3 天时间才弄清楚。

编辑:对于测试,一个更简单的方法是移出阻塞发送(同时位)并将其添加到协程中。似乎也可以这样工作。

于 2016-04-10T01:28:27.483 回答