2015-03-09 3 views
0

Я пытаюсь вывести все, что захвачено с веб-камеры, в Image control в окне WPF. Я пользуюсь библиотекой AForge.NET.Утечка памяти из ImageSource/CreateBitmapSourceFromHBitmap (при захвате веб-камеры с AForge)

К сожалению, после нескольких минут успешного захвата я получаю OutOfMemoryException. Аналогично, как только я начинаю захватывать, я вижу, что потребление памяти постоянно увеличивается в диспетчере задач до момента исключения (хотя было несколько случаев, когда использование памяти продолжало расти, а затем резко упало обратно в исходное состояние, а затем продолжал подниматься снова до точки исключения).

Это мой код для обработчика в NewFrame event в VideoCaptureDevice class (чей код для преобразования экземпляра Bitmap к ImageSource в значительной степени основана на an answer по Sascha Hennig):

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); 

private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs) 
{ 
    try 
    { 
     using (var streamBitmap = (Bitmap)eventArgs.Frame.Clone()) { 
      BitmapSource bitmapSourceVideo; 

      var hBitmap = streamBitmap.GetHbitmap(); 
      try 
      { 
       bitmapSourceVideo = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        hBitmap, 
        IntPtr.Zero, 
        Int32Rect.Empty, 
        BitmapSizeOptions.FromEmptyOptions()); 
      } 
      finally 
      { 
       DeleteObject(hBitmap); 
      } 
      bitmapSourceVideo.Freeze(); 

      Dispatcher.BeginInvoke(new ThreadStart(delegate 
                 { 
                  videoControl.Source = bitmapSourceVideo; 
                 })); 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.ToString()); 
    } 
} 

В случае, если интересно, требуется вызов eventArgs.Frame.Clone(). Пояснения можно найти here и, возможно, here.

При попытке изолировать источник этой проблемы, я комментировал различные части этого кода, пока я не прибыл в это состояние:

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); 

private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs) 
{ 
    try 
    { 
     using (var streamBitmap = (Bitmap)eventArgs.Frame.Clone()) { 
      BitmapSource bitmapSourceVideo; 

      var hBitmap = streamBitmap.GetHbitmap(); 
      try 
      {/* 
       bitmapSourceVideo = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
        hBitmap, 
        IntPtr.Zero, 
        Int32Rect.Empty, 
        BitmapSizeOptions.FromEmptyOptions()); 
      */} 
      finally 
      { 
       DeleteObject(hBitmap); 
      } 
      /* 
      bitmapSourceVideo.Freeze(); 

      Dispatcher.BeginInvoke(new ThreadStart(delegate 
                 { 
                  videoControl.Source = bitmapSourceVideo; 
                 }));*/ 
     } 
    } 
    catch (Exception ex) 
    { 
     MessageBox.Show(ex.ToString()); 
    } 
} 

(Очевидно, что это не что-нибудь нарисовать в окне , но это уже не так.) Эта версия метода не содержит утечки памяти. Удаление значков комментария в заявлении с вызовом CreateBitmapSourceFromHBitmap приводит к возврату памяти. Что мне здесь не хватает?

Там были различные источники о, казалось бы, подобных проблем, ни один из которых помогли мне найти решение:

  • This answer принимает на себя я загрузку с URI, который я мог бы вместо того, чтобы загрузить в поток.
  • This answer Предполагается, что я загружаю данные растрового изображения непосредственно из потока, доступ к которому я могу получить, а также this blogpost предлагает создать обертку потока.
  • Решение Freeze от this blogpost не должно применяться, так как утечка не изменяется в зависимости от того, комментирую или раскомментирую свой вызов на Freeze. Это дополнительно подтверждается this answer. не
  • This answer, this answer, this answer и all answers to this question указывают, что DeleteObject потребности, которая будет вызвана, как только ручки, полученной из GetHbitmap больше не нужен. Это также предлагается this blogpost. Я уже делаю это в коде.
  • Информация от this question предлагает мне отказаться от Bitmap, но я уже распоряжаюсь любым экземпляром Bitmap, который я создал самостоятельно, благодаря блоку using.
  • This forum thread звуки смутно похожи, но заканчиваются неубедительным образом.

ответ

0

Мой инстинкт говорит мне, что это связано с тем, что bitmapSourceVideo находится внутри делегата и поэтому создает закрытие и не очищается.

EDIT

Попробуйте

 Dispatcher.BeginInvoke(new ThreadStart(delegate 
     { 
      videoControl.Source = bitmapSourceVideo; 
      bitmapSourceVideo = null; 
     })); 
+0

Я посмотрел на это, но как именно я должен очистить вверх [ 'BitmapSource'] (https://msdn.microsoft.com/ ан-нас/библиотека/system.windows.media.imaging.bitmapsource% 2 = vs.110% 29.aspx)? Это не [одноразовый] (https://msdn.microsoft.com/en-us/libraRy/system.idisposable%28v=vs.100%29.aspx) или что-то в этом роде. [Этот ответ] (http://stackoverflow.com/a/11950108/1430156) (хотя это не принято ...) подразумевает, что дальнейшая очистка не требуется. –

+0

Также, что произойдет, если вы должны были обернуть весь этот метод внутри 'Dispatcher.BeginInvoke', а не только для установки источника? –

+0

Я могу попробовать, хотя я не ожидаю, что из этого выйдет что-нибудь хорошее - в частности, я был бы удивлен, если бы я мог полагаться на 'eventArgs.Frame', все еще находящийся вокруг после того, как обработчик событий завершился в потоке в который был вызван. –