Утечка памяти - использование MemoryStream для записи живого видео с камеры Nikon - PullRequest
2 голосов
/ 28 марта 2019

Я использую MemoryStream, чтобы получить JPEGBuffer, а затем декодирую его в Bitmap, используя JpegBitmapDecoder.Это приводит к увеличению использования памяти каждый раз, когда я начинаю читать живое видео с камеры Nikon.

DispatchTimer метод тиков написан ниже (запускается 30 раз каждую секунду):

private void dispatcherTimer_Tick(object sender, EventArgs e) {
 if (isLiveVideoEnabled) {
  try {
   Nikon.NikonLiveViewImage liveImageFromCamera = ((MainWindow) myParent).currentDevice.GetLiveViewImage();

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

    liveLoop.Set();
   }
  } catch (Nikon.NikonException ex) {
   MessageBox.Show(ex.Message);
  }
 }
}

Это код, когда пользователь нажимает Capture Button.Я прекращаю просмотр в режиме реального времени, а затем снимаю фотографию с Nikon.

ImageBehavior.AddAnimationCompletedHandler(imageControl,  async (sender , e) => {
          // Disabling Live Stream
          await liveLoop.WaitAsync();
          isLiveVideoEnabled = false;
          (myParent as MainWindow).currentDevice.LiveViewEnabled = false;

          //Starting to Capture Photo
          await (myParent as MainWindow).capturePhoto();
          await (myParent as MainWindow).waitForTheImage();
          string path = (myParent as MainWindow).getPhotoPath();
          Console.WriteLine("********************     " + path + "      *********************");
          System.Windows.Media.Imaging.BitmapImage bImage = new BitmapImage(new Uri(path));
          ImageBehavior.SetAnimatedSource(imageControl, null);
          (sender as Image).Source = bImage;
          Photos.imageDictionary[imageNumber] = path;
          //Enabling the Live Stream

          isLiveVideoEnabled = true;
          currentImageControl = imageControl;
          stopLoading();
          showRetry();
          (myParent as MainWindow).currentDevice.LiveViewEnabled = true;
     });

Это приложение работает в непрерывном режиме.

  1. Сенсорный экран для захвата фотографий
  2. Запускается просмотр в реальном времени, пользователь нажимает на GIF, который является счетчиком
  3. Как только цикл анимации GIF завершается, он останавливает просмотр в реальном времени и захватывает фотографию.
  4. Пользователь переходит в раздел обратной связи, и здесь завершается один цикл операций.

Когда я запускаю свое приложение, оно запускается с начальной памятью 67MB.Первый цикл работы увеличивает объем памяти до 185MB, и почти каждый 130MB добавляется при каждом запуске страницы просмотра в реальном времени.

Сначала я подумал, что проблема связана с WPF Pages, но я внимательно проверил использование памяти, оно увеличивается только тогда, когда мы запускаем живую камеру.Переключение на страницы не увеличивает память.

Я думаю, что использую неправильный подход для MemoryStream.Пожалуйста, руководство.

ОБНОВЛЕНИЕ 1 [dispatcherTimer_Tick код обновлен]:

Я реализовал решение Клеменса, введя using(MemoryStream) в методе dispatcherTimer_Tick.А также заморозил BitmapImage после BitmapImage.EndInit() Но потребление памяти такое же и без разницы.

Я запустил Profiler в VS и собрал следующую информацию, по крайней мере, сейчас я смотрю на вещи, о которых я должен позаботиться.

Исходное изображение, где я видел, что byte[] stream все еще подключено и не собрано GC.

First Profiler Image

После этого я началглядя дальше, чтобы добраться туда, куда это ведет.Вот другое изображение.(GetLiveViewImage от nikoncswrapper)

enter image description here

И последнее изображение с именем функции:

enter image description here

Теперь я думаю, что у меня есть больше информации, чтобы добраться до проблемы.Но я не могу понять, что еще я могу сделать.

Я даже создал новый метод для getBitmapImage:

public BitmapImage getBitmapFromURI(Uri uri) {
 var image = new BitmapImage();
 image.BeginInit();
 image.CacheOption = BitmapCacheOption.OnLoad;
 image.UriSource = uri;
 image.EndInit();
 image.Freeze();
 System.GC.Collect();
 GC.WaitForPendingFinalizers();
 return image;
}

1 Ответ

1 голос
/ 28 марта 2019

С BitmapCacheOption.None BitmapDecoder или BitmapFrame, который декодируется из потока, сохраняет поток открытым.

Поскольку MemoryStream является IDisposable, его следует утилизировать, предпочтительно, создав его внутри объявления использования. Однако это возможно только в том случае, если изображение декодируется немедленно с помощью параметра BitmapCacheOption.OnLoad.

.

Вам также не нужно явно использовать определенный BitmapDecoder. Просто используйте метод BitmapFrame.Create. Он автоматически выбирает соответствующий декодер.

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

Если проблема не устранена, вы можете взглянуть на этот ответ: https://stackoverflow.com/a/6271982/1136211 (хотя на самом деле немедленно декодированный BitmapFrame уже должен быть заморожен).

...