UWP - 通过Socket到MediaElement将WebCam流式传输 - 破碎的图片?

Det*_*ail 6 c# sockets video-streaming raspberry-pi2 uwp

背景

我编写的代码记录了来自网络摄像头的视频片段,将它们写入内存流,然后通过Socket连接传输数据,然后将其重新组合成视频并在媒体元素上播放.

最终目标是创建一个婴儿监视器系统,服务器/摄像头运行在Windows IOT Raspberry Pi上,以及我的女朋友和我可以在我们的手机或笔记本电脑上查看的UWP应用程序.除了从房子的另一部分观看摄像机外,我们还能够在我们其中一个人离家时登录,并且我还会及时连接PIR运动传感器和警报系统,但首先是第一.

整个系统工作得相当好,视频中有5秒的延迟,这是我可以接受的(现在),并且使用MediaPlaybackList,视频以相当恒定的速率无缝流式传输(尽可能无缝连接)现在)视频之间的转换.MediaPlaybackList在播放时删除项目,使内存占用保持相对恒定.

问题

当视频在客户端播放时,它会频繁出现但是随机的部分是破碎的图片.它没有任何模式,也不是我能找到的模式,我能描述的唯一方法是将图片的一部分水平分割成两半,两半交换,图片的右侧显示在左边,反之亦然.它就像一个闪烁,因为在破碎的位中只显示了几分之一秒,因为另一个在图片的其他地方出现了大约一秒左右.

这是一个例子:

在这里你可以看到框架的一部分处于错误的位置 现在,这里有几个有趣的点......

1)在我开始使用MediaPlaybackList排队数据流之前,我使用的方法是从传入的套接字流中提取每个视频,将其作为StorageFile保存到本地磁盘,然后排队这些StorageFiles,按顺序播放它们然后删除它们(我仍然有源代码控制中的这个代码的版本,我可以挖掘出来,但我不喜欢创建和销毁StorageFiles的想法,因为它看起来非常低效).然而,使用这种方法并没有导致我现在看到的破碎的图片...这让我相信视频本身很好,并且这可能是它被重新组合并流式传输的方式的问题媒体元素?

2)我的女朋友的猫把网络摄像头(微软Lifecam HD-3000)撞到了一边,没有我意识到,我没有意识到,直到我运行服务器并注意到图片是90度角...有趣(和这令人费解的是,传递给客户的图片没有像我上面描述的那样分解.我能看到的唯一不同之处在于图片的尺寸是480 x 640(来自相机坐在它的侧面),而不是标准的640 x 480.这意味着什么,我不确定......

关于这个问题的想法

  • 与视频的大小/尺寸有关(它在侧面播放得很好,所以它与此有关)?
  • 与比特率有关?
  • 与客户端重新组装字节的方式有关吗?
  • 与流的编码有关?

资源

这里有一些我认为可能相关的代码片段,完整的解决方案源可以在GitHub上找到,这里是: Video Socket Server .

服务器

while (true)
{
    try
    {
        //record a 5 second video to stream
        Debug.WriteLine($"Recording started");
        var memoryStream = new InMemoryRandomAccessStream();
        await _mediaCap.StartRecordToStreamAsync(MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Vga), memoryStream);
        await Task.Delay(TimeSpan.FromSeconds(5));
        await _mediaCap.StopRecordAsync();
        Debug.WriteLine($"Recording finished, {memoryStream.Size} bytes");

        //create a CurrentVideo object to hold stream data and give it a unique id
        //which the client app can use to ensure they only request each video once
        memoryStream.Seek(0);
        CurrentVideo.Id = Guid.NewGuid();
        CurrentVideo.Data = new byte[memoryStream.Size];

        //read the stream data into the CurrentVideo  
        await memoryStream.ReadAsync(CurrentVideo.Data.AsBuffer(), (uint)memoryStream.Size, InputStreamOptions.None);
        Debug.WriteLine($"Bytes written to stream");

        //signal to waiting connections that there's a new video
        _signal.Set();
        _signal.Reset();
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"StartRecording -> {ex.Message}");
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

连接

//use the guid to either get the current video, or wait for the 
//next new one that's added by the server
Guid guid = Guid.Empty;
Guid.TryParse(command, out guid);
byte[] data = _server.GetCurrentVideoDataAsync(guid);
if (data != null)
    await _socket.OutputStream.WriteAsync(data.AsBuffer());
Run Code Online (Sandbox Code Playgroud)

客户端应用

byte[] inbuffer = new byte[10000000];

//block on the input stream until we've received the full packet,
//but use the Partial option so that we don't have to fill the entire buffer before we continue.
//this is important, because the idea is to set the buffer big enough to handle any packet we'll receive,
//meaning we'll never fill the entire buffer... and we don't want to block here indefinitely
result = await socket.InputStream.ReadAsync(inbuffer.AsBuffer(), inbuffer.AsBuffer().Capacity, InputStreamOptions.Partial);

//strip off the Guid, leaving just the video data
byte[] guid = result.ToArray().Take(16).ToArray();
byte[] data = result.ToArray().Skip(16).ToArray();
_guid = new Guid(guid);

//wrap the data in a stream, create a MediaSource from it,
//then use that to create a MediaPlackbackItem which gets added 
//to the back of the playlist...
var stream = new MemoryStream(data);
var source = MediaSource.CreateFromStream(stream.AsRandomAccessStream(), "video/mp4");
var item = new MediaPlaybackItem(source);
_playlist.Items.Add(item);
Run Code Online (Sandbox Code Playgroud)

dea*_*dob 3

我想做类似的事情(从 Raspberry Pi 上的 UWP 应用程序流视频/音频),但我一直在使用 Windows 10 SDK 中的简单通信示例,经过一些调整后我已经能够开始工作可靠(示例代码存在线程同步问题)。然而,SDK 示例使用使用媒体扩展的专有协议,并且通过互联网重定向流并不容易,这是我的用例,因此我查看了您的代码并使其正常工作(具有相同的错误)。 简单的实时通信

对你的方法的一些评论:

1) RPi 不能很好地处理 Win10 上的视频,因为它不能使用硬件视频编码器,所以一切都在软件中完成。这会导致故障,我发现 CPU 性能显着提高,利用率超过 50%,这意味着至少有一个 CPU 核心正在接近最大值工作,可能是处理视频压缩为 MP4 的核心。不过,我运行了 SDK 示例并获得了无故障查看和大约 70% 的 CPU 利用率,因此您的问题可能出在其他地方。

2) 5 秒的延迟是很重要的。我在实时样本中获得了不到 100 毫秒的延迟,但是当我将流媒体计时器调整到 1 秒时,中断非常严重并且无法实现。您是否考虑过更改设计,以便在捕获期间进行流式传输,但我不确定 InMemoryRandomAccessStream 是否允许您这样做。另一种替代方法是捕获预览流并将自定义媒体接收器写入缓冲区(由于不是托管代码,因此很难做到,并且可能无法轻松压缩),就像简单通信示例一样。

3) MP4 是一种容器而不是压缩格式,并且不是为流式传输而构建的,因为必须在开始之前下载整个文件,除非将 moov 元数据记录放置在文件的开头。不确定 UWP 如何处理此问题,可能需要在发送之前关闭流的方法,以确保另一端可以正常播放。

所以这不是一个完整的答案,但希望以上内容有所帮助。