C# 内存泄漏-使用MemoryStream从尼康摄像头捕获实时视频

C# 内存泄漏-使用MemoryStream从尼康摄像头捕获实时视频,c#,wpf,memorystream,C#,Wpf,Memorystream,我正在使用MemoryStream获取JPEGBuffer,然后使用JpegBitmapDecoder将其解码为位图。这导致每次我开始阅读尼康摄像头的实时视频时,内存使用量都会增加 DispatchTimertick方法写在下面(每秒运行30次): private void dispatchermer_Tick(对象发送方,事件参数e){ 如果(isLiveVideoEnabled){ 试一试{ Nikon.NikonLiveViewImage liveImageFromCamera=((主窗口

我正在使用
MemoryStream
获取JPEGBuffer,然后使用
JpegBitmapDecoder
将其解码为位图。这导致每次我开始阅读尼康摄像头的实时视频时,内存使用量都会增加

DispatchTimer
tick方法写在下面(每秒运行30次):

private void dispatchermer_Tick(对象发送方,事件参数e){
如果(isLiveVideoEnabled){
试一试{
Nikon.NikonLiveViewImage liveImageFromCamera=((主窗口)myParent).currentDevice.GetLiveViewImage();
使用(var liveImageStream=newmemoryStream(liveImageFromCamera.JpegBuffer))
{
liveImageComponent.Source=BitmapFrame.Create(liveImageStream,BitmapCreateOptions.None,BitmapCacheOption.OnLoad);
liveLoop.Set();
}
}捕获(尼康,尼康例外){
MessageBox.Show(例如Message);
}
}
}
这是用户按下捕获按钮时的代码。我正在停止实况转播,然后从尼康那里抓拍照片

ImageBehavior.AddAnimationCompletedHandler(imageControl,异步(发送方,e)=>{
//禁用实时流
wait liveLoop.WaitAsync();
isLiveVideoEnabled=false;
(myParent作为主窗口)。currentDevice.LiveViewEnabled=false;
//开始抓拍照片
等待(myParent作为主窗口)。capturePhoto();
wait(作为主窗口的myParent).waitForTheImage();
字符串路径=(myParent作为主窗口).getPhotoPath();
Console.WriteLine(“**********************”+路径+“**********************”);
System.Windows.Media.Imaging.BitmapImage bImage=新BitmapImage(新Uri(路径));
SetAnimatedSource(imageControl,null);
(发送方为图像)。源=双图像;
Photos.imageDictionary[imageNumber]=路径;
//启用实时流
isLiveVideoEnabled=true;
currentImageControl=imageControl;
停止加载();
showRetry();
(myParent作为主窗口)。currentDevice.LiveViewEnabled=true;
});
此应用程序处于连续运行状态

  • 触摸屏拍摄照片
  • 实时查看开始,用户点击GIF,这是计数器
  • 一旦GIF动画循环完成,它就会停止实时视图,并捕获照片
  • 用户移动到反馈部分,一个操作周期在此完成 当我启动应用程序时,它从初始内存开始
    67MB
    。 第一个操作周期将内存增加到
    185MB
    ,并且每次启动Live View页面时几乎都会添加
    130MB

    首先,我认为问题在于
    WPF页面
    ,但我仔细检查了内存使用情况,只有当我们启动live camera时,内存使用才会增加。打开页面不会增加任何内存

    我想我对
    MemoryStream
    使用了错误的方法。请导游

    更新1[
    Dispatchermer\u勾选
    代码已更新]:

    我通过在
    Dispatchermer\u Tick
    方法中引入
    使用(MemoryStream)
    实现了Clemens解决方案。并且在
    BitmapImage.EndInit()
    之后冻结
    BitmapImage
    ,但是内存消耗是相同的,没有区别

    我在VS中启动了Profiler并收集了以下信息,至少现在我看到了我应该注意的事情

    我看到的
    byte[]stream
    的初始图像仍然是连接的,并且没有被GC收集

    从那以后,我开始寻找更进一步的方向。这是另一张图片。(
    GetLiveViewImage
    来自
    NikonSwraper

    最后一幅显示函数名的图像:

    现在我想我有更多的信息来解决这个问题。但我无法理解我还能做什么

    我甚至创建了一个新方法来
    getBitmapImage

    公共位图图像getBitmapFromURI(Uri){
    var image=新的位图图像();
    image.BeginInit();
    image.CacheOption=BitmapCacheOption.OnLoad;
    image.UriSource=uri;
    image.EndInit();
    image.Freeze();
    System.GC.Collect();
    GC.WaitForPendingFinalizers();
    返回图像;
    }
    
    使用
    BitmapCacheOption。无
    从流解码的BitmapDecoder或BitmapFrame使流保持打开状态

    由于MemoryStream是一个
    IDisposable
    ,因此最好通过在using声明中创建它来处理它。但是,只有通过设置
    BitmapCacheOption.OnLoad
    立即解码图像时,才可能执行此操作

    您也不需要显式使用特定的位图解码器。只需使用
    BitmapFrame.Create
    方法即可。它会自动选择合适的解码器

    using (var liveImageStream = new MemoryStream(liveImageFromCamera.JpegBuffer))
    {
        liveImageComponent.Source = BitmapFrame.Create(
            liveImageStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
        ...
    }
    

    如果仍然存在问题,您可以查看以下答案:(虽然立即解码的位图帧应该已经冻结)。

    您从未关闭并处理过流,您可以选择,但将其放入USE将以最简单的方式解决大部分问题。注意,
    如果(liveImageStream!=null)
    是没有意义的。您不需要检查
    new
    运算符的结果是否为null。但是,在未检查其结果是否为null的情况下,不能使用
    as
    运算符,就像在
    (myParent as MainWindow).currentDevice.GetLiveViewImage()
    中所做的那样。这实际上应该是
    ((MainWindow)myParent).currentDevice.GetLiveViewImage()
    。虽然当不再需要IDisPobles时,处理IDisPobles是一个好习惯,但对于
    内存流来说,这是不必要的。它不拥有任何可使用的资源,在处理时也不会释放内存。不过,这是一个好习惯,因为您可能最终会将
    内存流
    换成另一个需要处理的内存流。