将低延迟视频从 Raspberry Pi 流式传输到 UWP-APP 的最佳方式
Best way to stream low latency video from a Raspberry Pi to an UWP-APP
对于一个项目,我必须通过 TCP 从 UWP-APP
与 Raspberry Pi 零通信。因为 Raspberry 和带有接口的计算机都有一个私有 IP,所以我必须使用服务器将消息从一个客户端转发到另一个客户端。这部分已经可以工作了,但现在我的问题是我必须实现从 Raspberry 到 UWP-APP 的视频流。
因为我的合作伙伴负责创建和设计 UWP-APP,所以我用 WindowsForms
给自己做了一个小测试界面。我已经尝试了几种技术,例如 Netcat
通过服务器将视频输出到客户端或使用 raspivid
直接进行 TCP 流式传输,但迄今为止最好的解决方案是我在该项目中找到的 here.但是我没有使用 Eneter.Messaging-library
,而是使用自己的 class 与 TcpClients 进行通信。
我在 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() 方法。
在界面上,我将接收到的字节[]写入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[] 放入其中的方法。
首先,感谢您的快速回复和有趣的想法!
我查看了 Desktop Bridge,但它并不是我真正想要的,因为我的同事已经付出了很多努力来设计 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 中对我都有效。
对于一个项目,我必须通过 TCP 从 UWP-APP
与 Raspberry Pi 零通信。因为 Raspberry 和带有接口的计算机都有一个私有 IP,所以我必须使用服务器将消息从一个客户端转发到另一个客户端。这部分已经可以工作了,但现在我的问题是我必须实现从 Raspberry 到 UWP-APP 的视频流。
因为我的合作伙伴负责创建和设计 UWP-APP,所以我用 WindowsForms
给自己做了一个小测试界面。我已经尝试了几种技术,例如 Netcat
通过服务器将视频输出到客户端或使用 raspivid
直接进行 TCP 流式传输,但迄今为止最好的解决方案是我在该项目中找到的 here.但是我没有使用 Eneter.Messaging-library
,而是使用自己的 class 与 TcpClients 进行通信。
我在 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() 方法。
在界面上,我将接收到的字节[]写入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[] 放入其中的方法。
首先,感谢您的快速回复和有趣的想法!
我查看了 Desktop Bridge,但它并不是我真正想要的,因为我的同事已经付出了很多努力来设计 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 中对我都有效。