【问题标题】:Best way to stream low latency video from a Raspberry Pi to an UWP-APP将低延迟视频从 Raspberry Pi 流式传输到 UWP-APP 的最佳方式
【发布时间】:2021-02-04 21:07:43
【问题描述】:

对于一个项目,我必须通过 TCP 从UWP-APP 与 Raspberry Pi Zero 进行通信。因为树莓派和带有接口的计算机都有私有 IP,所以我必须使用服务器将消息从一个客户端转发到另一个客户端。这部分已经有效,但现在我的问题是我必须实现从 Raspberry 到 UWP-APP 的视频流。

因为我的伙伴负责创建和设计UWP-APP,所以我用WindowsForms给自己做了一个小测试接口。我尝试了几种技术,例如Netcat 通过服务器将视频输出到客户端或使用raspivid 直接进行 TCP 流式传输,但迄今为止最好的解决方案是我在这个项目中找到的那个here。但我没有使用Eneter.Messaging-library,而是使用我自己的类与 TcpClients 进行通信。

我使用 mono 在 Raspberry 上运行我的 C# 脚本,流式传输视频的代码如下所示:

while (true)
            {
                //Wait with streaming until the Interface is connected
                while (!RemoteDeviceConnected || VideoStreamPaused)
                {
                    Thread.Sleep(500);
                }
                //Check if Raspivid-Process is already running
                if(!Array.Exists(Process.GetProcesses(), p => p.ProcessName.Contains("raspivid")))
                    raspivid.Start();
                Thread.Sleep(2000);
                VideoData = new byte[VideoDataLength];
                try
                {
                    while (await raspivid.StandardOutput.BaseStream.ReadAsync(VideoData, 0, VideoDataLength) != -1 && !VideoChannelToken.IsCancellationRequested && RemoteDeviceConnected && !VideoStreamPaused)
                    {
                        // Send captured data to connected clients.
                        VideoConnection.SendByteArray(VideoData, VideoDataLength);
                    }
                    raspivid.Kill();
                    Console.WriteLine("Raspivid killed");
                }
                catch(ObjectDisposedException)
                {

                } 
            }

这个方法基本上只是从raspivid进程的Standard-Output-Stream中分块读取h264数据并发送给服务器。

下一个方法在服务器上运行,只是将字节数组转发给连接的接口客户端。

while (RCVVideo[id].Connected)
            {
                await RCVVideo[id].stream.ReadAsync(VideoData, 0, VideoDataLength);
                if (IFVideo[id] != null && IFVideo[id].Connected == true)
                {
                    IFVideo[id].SendByteArray(VideoData, VideoDataLength);
                }
            }

SendByteArray() 使用 NetworkStream.Write() 方法。

在接口上,我将接收到的 byte[] 写入命名管道,VLC-Control 连接到该管道:

while (VideoConnection.Connected)
            {
                await VideoConnection.stream.ReadAsync(VideoData, 0, VideoDataLength);
                if(VideoPipe.IsConnected)
                {
                    VideoPipe.Write(VideoData, 0, VideoDataLength);
                }
                
            }

以下代码初始化管道服务器:

// Open pipe that will be read by VLC.
        VideoPipe = new NamedPipeServerStream(@"\raspipipe",
                                                PipeDirection.Out, 1,
                                                PipeTransmissionMode.Byte,
                                                PipeOptions.WriteThrough, 0, 10000);

对于 VLC:

LibVLC libVLC = new LibVLC();

        videoView1.MediaPlayer = new MediaPlayer(libVLC);

        videoView1.MediaPlayer.Play(new Media(libVLC, @"stream/h264://\\\.\pipe\raspipipe", FromType.FromLocation));
        videoView1.MediaPlayer.EnableHardwareDecoding = true;
        videoView1.MediaPlayer.FileCaching = 0;
        videoView1.MediaPlayer.NetworkCaching = 300;

这在 Windowsforms-App 上运行良好,我可以将延迟降低到 2 或 3 秒(最终应该会更好,但可以接受)。但是在 UWP-App 上,即使将 /LOCAL/ 添加到管道名称后,我也无法使其工作。它显示VLC-Control连接到管道,我可以看到数据写入管道但它不显示视频。

所以我的问题是:

如何让它与 UWP 中的 VLC-Control (LibVLCSharp) 一起使用?我错过了一些基本的东西吗?

或者在这种情况下是否有更好的方法来流式传输视频?

我对 UWP-MediaPlayerElement 进行了一些研究,但找不到将我的 byte[] 放入其中的方法。

【问题讨论】:

  • 启用详细日志并分享它们
  • 检查 StreamMediaInput API
  • 您可以尝试使用 Desktop Bridge 技术将 Windows 窗体应用程序打包为 UWP 应用程序的桌面扩展组件。关于Desktop Bridge技术的介绍,可以参考document
  • 关于Desktop Bridge技术的详细步骤可以参考官方document或者Stefan Wick的UWP with Desktop Extension - Part 1的博客.

标签: c# uwp raspberry-pi video-streaming libvlcsharp


【解决方案1】:

首先,感谢您的快速回复和有趣的想法!

我查看了桌面桥,但它并不是我真正想要的,因为我的同事已经付出了很多努力来设计 UWP-APP,而我的 Windows-Form 只是一个拙劣的尝试。

但真正对我有用的是 StreamMediaInput 。我不知道我以前是怎么错过的。这样我就直接将NetworkStream 传递给MediaPlayer,而不使用命名管道。

LibVLC libVLC = new LibVLC();

videoView1.MediaPlayer = new MediaPlayer(libVLC);
Media streamMedia = new Media(libVLC, new StreamMediaInput(Client.Channels.VideoConnection.stream), ":demux=h264");
        
videoView1.MediaPlayer.EnableHardwareDecoding = true;
videoView1.MediaPlayer.FileCaching = 0;
videoView1.MediaPlayer.NetworkCaching = 500; 

videoView1.MediaPlayer.Play(streamMedia);

这个解决方案现在在 UWP 和 Windows-Forms 中都适用于我。

【讨论】:

    猜你喜欢
    • 2022-10-14
    • 1970-01-01
    • 1970-01-01
    • 2014-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 2012-01-25
    相关资源
    最近更新 更多