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

背景

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

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

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

问题

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

这是一个例子:

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

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

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

关于这个问题的想法

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

资源

以下是我认为可能相关的几段代码,完整的解决方案源可以在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; } } 

连接

 //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()); 

客户端应用

 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); 

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

关于您的方法的几点评论:

1)RPi无法很好地处理Win10上的video,因为它无法使用硬件video编码器,因此无需使用软件。 这将导致故障,我看到CPU性能显着提高,利用率超过50%,这意味着至少有一个CPU核心工作接近最大值,可能是处理video压缩到MP4的那个。 但是我运行了SDK示例并且无故障查看和大约70%的CPU,因此您的问题可能在其他地方。

2)5秒的延迟延迟是显着的。 我使用实时样本获得的延迟不到100毫秒但是当我将流式定时器调低到1秒时,分解很重要且不可行。 您是否考虑过更改设计以便在捕获期间进行流式传输但是我不确定InMemoryRandomAccessStream是否会让您这样做。 另一种方法是捕获预览流并编写一个自定义媒体接收器来缓冲(比较难以管理代码并且可能无法轻松压缩),就像Simple Communication示例那样。

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

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