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

时间:2016-06-14 12:47:59

标签: c# sockets video-streaming uwp raspberry-pi2

背景

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

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

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

问题

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

以下是一个例子:

Here you can see part of the frame is in the wrong position 现在,这里有几个有趣的点......

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

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

对问题的想法

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

来源

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

1 个答案:

答案 0 :(得分:2)

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

对您的方法提出几点意见:

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

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

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

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