Windows Phone使用UDP传输摄像头数据非常缓慢

5

我一直在做一个私人项目,想要学习如何在Windows手机上编程。在尝试使用套接字和摄像头时,我产生了一个非常好的想法 - 视频流。

但是现在我遇到了问题,Lumia 800不能够快速地处理for-loop,每7-8秒只发送一帧画面,这让我感到困惑,因为应该足够快。它看起来就像在56k调制解调器上观看色情片而没有真正的色情内容。

我也意识到一帧有317,000个像素,大约占用1MB的空间。我还发送xy坐标,所以每帧总共需要2.3MB。我正在寻找其他方法来降低数据量。如果要获得可接受的速度,则需要施展魔术来使位置和像素值变为合理的大小。因为目前要达到可接受的速度至少需要60MB/s才能获得30fps,但这是另一个问题了。

//How many pixels to send per burst (1000 seems to be the best)
    const int PixelPerSend = 1000;
    int bSize = 7 * PixelPerSend;
    //Comunication thread UDP feed   
    private void EthernetComUDP() //Runs in own thread
    {
        //Connect to Server
        clientUDP = new SocketClientUDP();
        int[] ImageContent = new int[(int)cam.PreviewResolution.Height *   (int)cam.PreviewResolution.Width];
        byte[] PacketContent = new byte[bSize];
        string Pixel,l;
        while (SendingData)
        {
            cam.GetPreviewBufferArgb32(ImageContent);
            int x = 1, y = 1, SenderCount = 0;
            //In dire need of a speedup
            for (int a = 0; a < ImageContent.Length; a++) //this loop
            {
                Pixel = Convert.ToString(ImageContent[a], 2).PadLeft(32, '0');

                //A - removed to conserve bandwidth
                //PacketContent[SenderCount] = Convert.ToByte(Pixel.Substring(0, 8), 2);//0
                //R
                PacketContent[SenderCount] = Convert.ToByte(Pixel.Substring(8, 8), 2);//8
                //G
                PacketContent[SenderCount + 1] = Convert.ToByte(Pixel.Substring(16, 8), 2);//16
                //B
                PacketContent[SenderCount + 2] = Convert.ToByte(Pixel.Substring(24, 8), 2);//24

                //Coordinates
                //X
                l = Convert.ToString(x, 2).PadLeft(16, '0');
                //X bit(1-8)
                PacketContent[SenderCount + 3] = Convert.ToByte(l.Substring(0, 8), 2);
                //X bit(9-16)
                PacketContent[SenderCount + 4] = Convert.ToByte(l.Substring(8, 8), 2);

                //Y
                l = Convert.ToString(y, 2).PadLeft(16, '0');
                //Y bit(1-8)
                PacketContent[SenderCount + 5] = Convert.ToByte(l.Substring(0, 8), 2);
                //Y bit(9-16)
                PacketContent[SenderCount + 6] = Convert.ToByte(l.Substring(8, 8), 2);

                x++;
                if (x == cam.PreviewResolution.Width)
                {
                    y++;
                    x = 1;
                }

                SenderCount += 7;
                if (SenderCount == bSize)
                {
                    clientUDP.Send(ConnectToIP, PORT + 1, PacketContent);
                    SenderCount = 0;
                }
            }
        }
        //Close on finish
        clientUDP.Close();
    }

我尝试简化操作,只是单独发送像素。使用以下方法:
BitConverter.GetBytes(ImageContent[a]);

我创建了一个混乱的字符串解析方法(仅为证明概念而修复),但使用简单的BitConverter并没有使其加速太多。

所以现在我的最后一个想法是使用与MSDN库中大致相同的UDP发送套接字。

    public string Send(string serverName, int portNumber, byte[] payload)
    {
        string response = "Operation Timeout";
        // We are re-using the _socket object that was initialized in the Connect method
        if (_socket != null)
        {
            // Create SocketAsyncEventArgs context object
            SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
            // Set properties on context object
            socketEventArg.RemoteEndPoint = new DnsEndPoint(serverName, portNumber);
            // Inline event handler for the Completed event.
            // Note: This event handler was implemented inline in order to make this method self-contained.
            socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(delegate(object s, SocketAsyncEventArgs e)
            {
                response = e.SocketError.ToString();
                // Unblock the UI thread
                _clientDone.Set();
            });
            socketEventArg.SetBuffer(payload, 0, payload.Length);
            // Sets the state of the event to nonsignaled, causing threads to block
            _clientDone.Reset();
            // Make an asynchronous Send request over the socket
            _socket.SendToAsync(socketEventArg);
            // Block the UI thread for a maximum of TIMEOUT_MILLISECONDS milliseconds.
            // If no response comes back within this time then proceed
            _clientDone.WaitOne(TIMEOUT_MILLISECONDS);
        }
        else
        {
            response = "Socket is not initialized";
        }
        return response;
    }

总的来说,我已经得出了3个解决方案:

  1. 接受失败(但这不可能发生,所以让我们看看第二个方案)

  2. 减少发送的数据量(会破坏质量,640x480足够小了)

  3. 找到明显的问题(谷歌和朋友们都没有好主意,所以我来了)


你确定不是网络带宽达到了上限吗?你的物理和逻辑网络层是什么? - TomTom
通过USB线和WiFi,我读取的最大速度是500Kb/s,难道不应该更快吗? - Thomas Andreè Wang
将问题分解 - 如果忽略相机并重复发送相同的单帧,您会得到什么帧率?如果将数据流式传输到文件中,您会得到什么帧率?如果只发送帧的前5k左右,会发生什么?由于没有alpha通道,您是否需要使用Argb? - James Snell
我尝试了发送第一个像素,但实际上没有任何变化。重复发送相同的像素比像代码示例中一样爆发1000和1000更慢。是的,我需要使用Argb,这是获取预览缓冲区的唯一方法,除了YCbCr和Y仅预览缓冲区。 - Thomas Andreè Wang
2个回答

2
问题几乎肯定是在处理数据方面。将一兆字节的二进制数据转换为数兆字节的文本,然后提取和发送单个字符将会增加每个源数据字节的大量开销。循环遍历单个像素以构建发送缓冲区将需要(相对而言)地质时间尺度。
最快的方法可能是从相机获取一个二进制数据缓冲区,并使用一个UDP写操作将其发送。只有在必要时才在手机上处理或拆分数据,并小心直接访问原始二进制数据 - 不要将所有数据都转换为字符串再转回二进制。您在此过程中添加的每个额外方法调用都将增加开销。如果必须使用循环,请尽量预先计算尽可能多的内容以最小化每次迭代所执行的工作。

哈哈,这让我理解了需要大幅提高速度的方法,现在我至少可以获得5-8帧每秒,谢谢。 - Thomas Andreè Wang

0
有几件事情需要考虑:#1 将原始图像数组分成多个部分发送。不确定 Windows Phone 上是否可用 Linq,但可以使用类似 this 的东西。
#2 由于处理时间和内存使用量,从 int 转换为 string 再转换为 byte 将非常低效。更好的方法是直接将 int 数组的块批量复制到 byte 数组中。Example

#1 我正在做这件事,我正在填充一个由x*7个元素组成的byte[]数组,并且每隔x次发送一次,这个x是在代码的第一行中定义的PixelPerSend。#2 我尝试了在两段代码之间解释的方法(当我使用BitConvert时,我只转换了像素数据并没有发送其他任何东西,但我只获得了10-15%的速度提升,所以这不是问题)。 - Thomas Andreè Wang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接