使用 Image.FromStream() 时出现“参数无效”异常 - UDP 视频直播

0 投票
1 回答
26 浏览
提问于 2025-04-13 15:14

我正在尝试将树莓派的摄像头直播到电脑上,使用的是UDP协议。我参考了Picamera2库提供的示例代码,用Python发送直播数据,并在电脑上使用C#的WinForms中的PictureBox来显示这个直播。

当C#应用接收到数据的字节数组后,我使用内存流和Image.FromStream()来把字节数组转换成图片。不过,这时出现了一个错误,提示“参数无效”。

Python代码(服务器):

host = #IP Address
port = 24325

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server_socket:
    # Initial connection
    server_socket.bind((host, port))
    print("UDP Server is listening...")

    while True:
        data, addr = server_socket.recvfrom(1024)
        rx = data.decode()

        ## Protocol Code...
        # if...

        # Stream code
        elif rx == "STREAM":
            tx = "STREAMING"
            server_socket.sendto(tx.encode(), addr)
                
            print("Starting stream")
                
            # Note: 2 cameras are being used on the Pi but only 1 is being streamed for testing
            picam1 = Picamera2(0)
            picam2 = Picamera2(1)
                
            config1 = picam1.create_preview_configuration()
            config2 = picam2.create_preview_configuration()
            picam1.configure(config1)
            picam2.configure(config2)

            picam1.controls.ExposureTime = 30000
            picam2.controls.ExposureTime = 30000
            encoder = H264Encoder(1000000)
                
            server_socket.connect(addr)
                
            stream = server_socket.makefile("wb")
            picam1.start_recording(encoder, FileOutput(stream))
        else:
            print(f"Recieved from {addr}: {rx}")
            tx = "Your message received"
            server_socket.sendto(tx.encode(), addr)

C#代码(客户端):

// Connection code...
// Protocol code...

// Receive livestream data
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, 0);
bool isReceiving = true;

if (rx == "STREAMING")
{
    logToConsole("Stream started");    // Custom function

    try
    {
        while (isReceiving)
        {
            byte[] streamData = udpClient.Receive(ref serverEndPoint);

            using (MemoryStream ms = new MemoryStream(streamData))
            {
                Image receivedImage = Image.FromStream(ms);

                if (receivedImage != null)
                {
                    pictureBoxStream.Invoke(new Action(() => pictureBoxStream.Image = receivedImage));
                }
            }
        }
    }
    catch (Exception ex)
    {
        logToConsole("Error receiving video stream: " + ex.Message);
    }
}

1 个回答

1

我不是Python开发者,可能有些地方理解错了,但我觉得这里有几个基本问题:

没有消息重组

一个UDP数据包的最大大小大约是65KB。接收方假设每个数据包都是一个完整的帧,但除非图像非常小,否则根本无法保证这一点。

通常,你会在UDP之上定义一个协议,这样可以发送和重组更大的消息,还能处理数据包乱序到达的问题。同时,它还需要考虑丢失的数据包,可以通过某种重发机制或者内置冗余来解决。

没有解压缩

看起来Python代码使用了h264压缩,但在C#中没有解码这种视频流的东西。Image.FromStream可以处理大多数常见的图像格式,比如jpeg和png,但对于大多数真实的视频流,你需要一个第三方解码器。

过早关闭流

根据Image.FromStream的文档:

你必须在图像的生命周期内保持流的打开状态。

所以要去掉using

建议

我建议使用一些现有的协议。实时流协议可能是个不错的选择,它建立在UDP之上,并且应该内置处理丢失数据包、乱序传输等问题。

另一个选择可能是像通过http、gRPC或类似方式发送jpeg帧,这样可以保证可靠传输,而且使用起来相对简单。

撰写回答