Я пытаюсь вывести все, что захвачено с веб-камеры, в 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 звуки смутно похожи, но заканчиваются неубедительным образом.
Я посмотрел на это, но как именно я должен очистить вверх [ '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) (хотя это не принято ...) подразумевает, что дальнейшая очистка не требуется. –
Также, что произойдет, если вы должны были обернуть весь этот метод внутри 'Dispatcher.BeginInvoke', а не только для установки источника? –
Я могу попробовать, хотя я не ожидаю, что из этого выйдет что-нибудь хорошее - в частности, я был бы удивлен, если бы я мог полагаться на 'eventArgs.Frame', все еще находящийся вокруг после того, как обработчик событий завершился в потоке в который был вызван. –