如何修复 GetPreviewFrameAsync 中的内存泄漏
How to fix memory leak in GetPreviewFrameAsync
我有基于此示例的代码; https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/CameraGetPreviewFrame。每次都行;
await _mediaCapture.GetPreviewFrameAsync(_currentVideoFrame);
被打到我们好像内存泄漏了。我没有收拾什么?我也尝试过每次创建模板框架并在每个循环中处理和清零它 - 这似乎也不起作用。
我已经回到微软的原始样本,它似乎也泄漏了。这是我的代码;
await Task.Run(async () =>
{
try
{
var videoEncodingProperties =
_mediaCapture.VideoDeviceController.GetMediaStreamProperties
(MediaStreamType.VideoPreview) as VideoEncodingProperties;
Debug.Assert(videoEncodingProperties != null, nameof(videoEncodingProperties) + " != null");
_currentVideoFrame = new VideoFrame(BitmapPixelFormat.Gray8,
(int) videoEncodingProperties.Width,
(int) videoEncodingProperties.Height);
TimeSpan? lastFrameTime = null;
while (_mediaCapture.CameraStreamState == CameraStreamState.Streaming)
{
token.ThrowIfCancellationRequested();
await _mediaCapture.GetPreviewFrameAsync(_currentVideoFrame);
if (!lastFrameTime.HasValue ||
lastFrameTime != _currentVideoFrame.RelativeTime)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync
(CoreDispatcherPriority.Normal,
() =>
{
try
{
Debug.Assert(_currentVideoFrame != null,
$"{nameof(_currentVideoFrame)} != null");
var bitmap = _currentVideoFrame.SoftwareBitmap.AsBitmap();
float focalLength = _cameraOptions == CameraOptions.Front ? AppSettings.FrontCameraFocalLength : AppSettings.RearCameraFocalLength;
_frameProcessor.ProcessFrame(bitmap, focalLength);
}
catch (Exception ex)
{
Debug.WriteLine($"Exception: {ex}");
}
});
lastFrameTime = _currentVideoFrame.RelativeTime;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Exception: {ex}");
}
},
token);
这应该简单地获取帧并将它们放入 _frameProcessor.ProcessFrame()
调用,但即使什么都不做(我删除了除 GetPreviewFrameAsync 之外的所有内容)它也会泄漏。
要重复问题,请从以下位置下载示例; https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/CameraGetPreviewFrame。 运行 带有诊断工具(Debug->Windows->Show Diagnostic tools)的调试器中的示例远程到 Windows 10 v 下的 Surface Pro 4 (i5-6300U @2.4GHz) 1903 (18362.175)。打开显示帧复选框并在按下 GetPreviewFrameAsync 按钮时观察内存。内存如下所示,每次上升都是我按下按钮;
使用 MediaFrameReader API works around this bug well in our code, and is perhaps slightly more responsive. Microsoft have now added a note to the GetPreviewFrameAsync documentation page 指向这个。
这对我们有用;
...
private MediaFrameReader _mediaFrameReader;
...
private async Task InitializeCameraAsync()
{
if (_mediaCapture == null)
{
_mediaCapture = new MediaCapture();
var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();
var selectedGroup = frameSourceGroups.FirstOrDefault(x => x.Id.Equals(_camera.UwpDeviceInfo.Id));
try
{
var mediaInitSettings = new MediaCaptureInitializationSettings
{
SourceGroup = selectedGroup,
VideoDeviceId = _camera.UwpDeviceInfo.Id,
AudioDeviceId = string.Empty,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};
await _mediaCapture.InitializeAsync(mediaInitSettings);
_isInitialized = true;
}
catch (UnauthorizedAccessException)
{
...
}
catch (Exception ex)
{
...
}
...
// Set-up for frameProcessing
var sourceInfo = selectedGroup?.SourceInfos.FirstOrDefault(info =>
info.SourceKind == MediaFrameSourceKind.Color);
...
var colorFrameSource = _mediaCapture.FrameSources[sourceInfo.Id];
var preferredFormat = colorFrameSource.SupportedFormats
.OrderByDescending(x => x.VideoFormat.Width)
.FirstOrDefault(x => x.VideoFormat.Width <= 1920 &&
x.Subtype.Equals(MediaEncodingSubtypes.Nv12, StringComparison.OrdinalIgnoreCase));
await colorFrameSource.SetFormatAsync(preferredFormat);
_mediaFrameReader = await _mediaCapture.CreateFrameReaderAsync(colorFrameSource);
}
...
}
...
private void _mediaFrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
...
var mediaFrameReference = sender.TryAcquireLatestFrame();
var videoMediaFrame = mediaFrameReference?.VideoMediaFrame;
var softwareBitmap = videoMediaFrame?.SoftwareBitmap;
if (softwareBitmap != null && _frameProcessor != null)
{
if (_mediaCapture.CameraStreamState == CameraStreamState.Streaming)
{
...
_frameProcessor.ProcessFrame(SoftwareBitmap.Convert(softwareBitmap,
BitmapPixelFormat.Gray8).AsBitmap(), _camera);
...
}
}
...
}
}
我有基于此示例的代码; https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/CameraGetPreviewFrame。每次都行;
await _mediaCapture.GetPreviewFrameAsync(_currentVideoFrame);
被打到我们好像内存泄漏了。我没有收拾什么?我也尝试过每次创建模板框架并在每个循环中处理和清零它 - 这似乎也不起作用。
我已经回到微软的原始样本,它似乎也泄漏了。这是我的代码;
await Task.Run(async () =>
{
try
{
var videoEncodingProperties =
_mediaCapture.VideoDeviceController.GetMediaStreamProperties
(MediaStreamType.VideoPreview) as VideoEncodingProperties;
Debug.Assert(videoEncodingProperties != null, nameof(videoEncodingProperties) + " != null");
_currentVideoFrame = new VideoFrame(BitmapPixelFormat.Gray8,
(int) videoEncodingProperties.Width,
(int) videoEncodingProperties.Height);
TimeSpan? lastFrameTime = null;
while (_mediaCapture.CameraStreamState == CameraStreamState.Streaming)
{
token.ThrowIfCancellationRequested();
await _mediaCapture.GetPreviewFrameAsync(_currentVideoFrame);
if (!lastFrameTime.HasValue ||
lastFrameTime != _currentVideoFrame.RelativeTime)
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync
(CoreDispatcherPriority.Normal,
() =>
{
try
{
Debug.Assert(_currentVideoFrame != null,
$"{nameof(_currentVideoFrame)} != null");
var bitmap = _currentVideoFrame.SoftwareBitmap.AsBitmap();
float focalLength = _cameraOptions == CameraOptions.Front ? AppSettings.FrontCameraFocalLength : AppSettings.RearCameraFocalLength;
_frameProcessor.ProcessFrame(bitmap, focalLength);
}
catch (Exception ex)
{
Debug.WriteLine($"Exception: {ex}");
}
});
lastFrameTime = _currentVideoFrame.RelativeTime;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Exception: {ex}");
}
},
token);
这应该简单地获取帧并将它们放入 _frameProcessor.ProcessFrame()
调用,但即使什么都不做(我删除了除 GetPreviewFrameAsync 之外的所有内容)它也会泄漏。
要重复问题,请从以下位置下载示例; https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/CameraGetPreviewFrame。 运行 带有诊断工具(Debug->Windows->Show Diagnostic tools)的调试器中的示例远程到 Windows 10 v 下的 Surface Pro 4 (i5-6300U @2.4GHz) 1903 (18362.175)。打开显示帧复选框并在按下 GetPreviewFrameAsync 按钮时观察内存。内存如下所示,每次上升都是我按下按钮;
使用 MediaFrameReader API works around this bug well in our code, and is perhaps slightly more responsive. Microsoft have now added a note to the GetPreviewFrameAsync documentation page 指向这个。
这对我们有用;
...
private MediaFrameReader _mediaFrameReader;
...
private async Task InitializeCameraAsync()
{
if (_mediaCapture == null)
{
_mediaCapture = new MediaCapture();
var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();
var selectedGroup = frameSourceGroups.FirstOrDefault(x => x.Id.Equals(_camera.UwpDeviceInfo.Id));
try
{
var mediaInitSettings = new MediaCaptureInitializationSettings
{
SourceGroup = selectedGroup,
VideoDeviceId = _camera.UwpDeviceInfo.Id,
AudioDeviceId = string.Empty,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};
await _mediaCapture.InitializeAsync(mediaInitSettings);
_isInitialized = true;
}
catch (UnauthorizedAccessException)
{
...
}
catch (Exception ex)
{
...
}
...
// Set-up for frameProcessing
var sourceInfo = selectedGroup?.SourceInfos.FirstOrDefault(info =>
info.SourceKind == MediaFrameSourceKind.Color);
...
var colorFrameSource = _mediaCapture.FrameSources[sourceInfo.Id];
var preferredFormat = colorFrameSource.SupportedFormats
.OrderByDescending(x => x.VideoFormat.Width)
.FirstOrDefault(x => x.VideoFormat.Width <= 1920 &&
x.Subtype.Equals(MediaEncodingSubtypes.Nv12, StringComparison.OrdinalIgnoreCase));
await colorFrameSource.SetFormatAsync(preferredFormat);
_mediaFrameReader = await _mediaCapture.CreateFrameReaderAsync(colorFrameSource);
}
...
}
...
private void _mediaFrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
...
var mediaFrameReference = sender.TryAcquireLatestFrame();
var videoMediaFrame = mediaFrameReference?.VideoMediaFrame;
var softwareBitmap = videoMediaFrame?.SoftwareBitmap;
if (softwareBitmap != null && _frameProcessor != null)
{
if (_mediaCapture.CameraStreamState == CameraStreamState.Streaming)
{
...
_frameProcessor.ProcessFrame(SoftwareBitmap.Convert(softwareBitmap,
BitmapPixelFormat.Gray8).AsBitmap(), _camera);
...
}
}
...
}
}