fix: CameraService view finder

Image capturing while view finder work
This commit is contained in:
2026-02-09 09:42:39 +03:00
parent 47b8d60c52
commit 47ca758fbb
6 changed files with 43 additions and 24 deletions
+17 -7
View File
@@ -79,6 +79,8 @@ public class CameraService : IDisposable
private List<MappedBuffer>? _imageCaptureMappedBuffers; private List<MappedBuffer>? _imageCaptureMappedBuffers;
private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource(); private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
public bool IsImageCapturing { get; private set; } = false;
public CameraService(ILogger<CameraService> logger, CameraServiceConfiguration? configuration = null, bool autoInitialize = false) public CameraService(ILogger<CameraService> logger, CameraServiceConfiguration? configuration = null, bool autoInitialize = false)
{ {
_logger = logger; _logger = logger;
@@ -488,6 +490,8 @@ public class CameraService : IDisposable
.CreateLinkedTokenSource(_disposeCts.Token, cancellationToken) .CreateLinkedTokenSource(_disposeCts.Token, cancellationToken)
.Token; .Token;
IsImageCapturing = true;
_logger.LogDebug("Capturing {} frame(s)", framesCount); _logger.LogDebug("Capturing {} frame(s)", framesCount);
OpenCvSharp.Mat? image = null; OpenCvSharp.Mat? image = null;
@@ -501,8 +505,6 @@ public class CameraService : IDisposable
{ {
effectiveCancellationToken.ThrowIfCancellationRequested(); effectiveCancellationToken.ThrowIfCancellationRequested();
_logger.LogDebug("Requested {} frame(s), now collected {}", framesCount, planesData.Count()); _logger.LogDebug("Requested {} frame(s), now collected {}", framesCount, planesData.Count());
if (File.Exists("/proc/self/status"))
_logger.LogDebug("Current RAM Usage: {}", File.ReadAllLines("/proc/self/status").FirstOrDefault(l => l.StartsWith("RssAnon:"))?.Substring("RssAnon:".Length)?.Trim() ?? "UNKNOWN");
var oldPlanesDataCount = planesData.Count(); var oldPlanesDataCount = planesData.Count();
@@ -521,7 +523,7 @@ public class CameraService : IDisposable
throw new Exception("Error while request create"); throw new Exception("Error while request create");
requests.Add(request); requests.Add(request);
} }
_logger.LogDebug("Request(s) created"); _logger.LogDebug("{} request(s) created", requests.Count());
effectiveCancellationToken.ThrowIfCancellationRequested(); effectiveCancellationToken.ThrowIfCancellationRequested();
@@ -578,6 +580,8 @@ public class CameraService : IDisposable
planesData.Add(planeData); planesData.Add(planeData);
} }
_logger.LogDebug("Planes data added");
if (planesData.Count() - oldPlanesDataCount == requests.Count()) if (planesData.Count() - oldPlanesDataCount == requests.Count())
cts.SetResult(); cts.SetResult();
} }
@@ -592,8 +596,9 @@ public class CameraService : IDisposable
throw new Exception("Error while request queue"); throw new Exception("Error while request queue");
} }
} }
_logger.LogDebug("Request(s) queued"); _logger.LogDebug("{} request(s) queued", requests.Count());
await cts.Task.WaitAsync(effectiveCancellationToken); await cts.Task.WaitAsync(effectiveCancellationToken);
_logger.LogDebug("{} request(s) compilted", requests.Count());
_camera.RequestCompleted -= RequestCompleted; _camera.RequestCompleted -= RequestCompleted;
foreach (var key in fdsData.Keys) foreach (var key in fdsData.Keys)
@@ -604,9 +609,6 @@ public class CameraService : IDisposable
} }
_logger.LogDebug("Frame(s) collected"); _logger.LogDebug("Frame(s) collected");
if (File.Exists("/proc/self/status"))
_logger.LogDebug("Current RAM Usage: {}", File.ReadAllLines("/proc/self/status").FirstOrDefault(l => l.StartsWith("RssAnon:"))?.Substring("RssAnon:".Length)?.Trim() ?? "UNKNOWN");
effectiveCancellationToken.ThrowIfCancellationRequested(); effectiveCancellationToken.ThrowIfCancellationRequested();
@@ -657,6 +659,8 @@ public class CameraService : IDisposable
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
planesData.Clear(); planesData.Clear();
} }
IsImageCapturing = false;
return image; return image;
} }
@@ -707,6 +711,9 @@ public class CameraService : IDisposable
void TimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea) void TimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea)
{ {
if (IsImageCapturing)
return;
try try
{ {
FrameBuffer buffer; FrameBuffer buffer;
@@ -766,6 +773,9 @@ public class CameraService : IDisposable
} }
public (FrameBuffer, StreamConfiguration)? GrabViewFinderFrame() public (FrameBuffer, StreamConfiguration)? GrabViewFinderFrame()
{ {
if (IsImageCapturing)
return null;
FrameBuffer? buffer = null; FrameBuffer? buffer = null;
lock (ViewFinderBufferQueue) lock (ViewFinderBufferQueue)
if (ViewFinderBufferQueue.Count() > 0) if (ViewFinderBufferQueue.Count() > 0)
+1 -1
View File
@@ -23,7 +23,7 @@ public partial class MainViewModel : ViewModelBase
ResourcesViewModel = resourcesViewModel; ResourcesViewModel = resourcesViewModel;
ButtonClick = new RelayCommand(() => ButtonClick = new RelayCommand(() =>
{ {
cameraService.CaptureImage(CancellationToken.None, 10).ContinueWith(image => Image = image.Result); Task.Run(() => cameraService.CaptureImage(CancellationToken.None, 10).ContinueWith(image => Image = image.Result));
}); });
_logger.LogInformation("{} initialized", nameof(MainViewModel)); _logger.LogInformation("{} initialized", nameof(MainViewModel));
+7 -5
View File
@@ -6,10 +6,12 @@
xmlns:core="using:GSS2.UI.Core" xmlns:core="using:GSS2.UI.Core"
x:DataType="local_vm:MainViewModel" x:DataType="local_vm:MainViewModel"
x:Class="GSS2.Test.Views.MainView"> x:Class="GSS2.Test.Views.MainView">
<Grid ColumnDefinitions="*,Auto"> <Grid RowDefinitions="*,*">
<core:OpenCvImage Grid.Column="0" Image="{Binding Image}" /> <Grid Grid.Row="0" ColumnDefinitions="*,*,Auto">
<Button Grid.Column="1" Width="50" VerticalAlignment="Stretch" Command="{Binding ButtonClick}"/> <ContentControl Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding CameraViewModel}" />
<!-- <ContentControl Grid.Row="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding CameraViewModel}" /> --> <core:OpenCvImage Grid.Column="1" Image="{Binding Image}" />
<!-- <ContentControl Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding ResourcesViewModel}" /> --> <Button Grid.Column="2" Width="50" VerticalAlignment="Stretch" Command="{Binding ButtonClick}"/>
</Grid>
<ContentControl Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding ResourcesViewModel}" />
</Grid> </Grid>
</UserControl> </UserControl>
+2 -2
View File
@@ -39,8 +39,8 @@
"Camera": { "Camera": {
"CameraId": "", "CameraId": "",
"ViewFinderFrameBufferCount": 5, "ViewFinderFrameBufferCount": 5,
"ViewFinderWidth": 1920, "ViewFinderWidth": 4056,
"ViewFinderHeight": 1080, "ViewFinderHeight": 3040,
"ViewFinderFps": 30, "ViewFinderFps": 30,
"ImageCaptureFrameBufferCount": 2, "ImageCaptureFrameBufferCount": 2,
"ImageCaptureWidth": 4056, "ImageCaptureWidth": 4056,
+6 -1
View File
@@ -28,7 +28,11 @@ public partial class CameraViewModel : ViewModelBase, IDisposable
_cameraService = cameraService; _cameraService = cameraService;
_logger = logger; _logger = logger;
_renderTimer = new System.Timers.Timer(33); _renderTimer = new System.Timers.Timer(33);
_renderTimer.Elapsed += (_, _) => RenderReqested?.Invoke(this, new EventArgs()); _renderTimer.Elapsed += (_, _) =>
{
if (!_cameraService.IsImageCapturing)
RenderReqested?.Invoke(this, new EventArgs());
};
_logger.LogInformation("{} initialized", nameof(CameraViewModel)); _logger.LogInformation("{} initialized", nameof(CameraViewModel));
} }
@@ -42,6 +46,7 @@ public partial class CameraViewModel : ViewModelBase, IDisposable
_renderTimer.Stop(); _renderTimer.Stop();
_cameraService.StopViewFinder(); _cameraService.StopViewFinder();
} }
public bool CanGrabViewFinderFrame() => !_cameraService.IsImageCapturing;
public (FrameBuffer, StreamConfiguration)? GrabViewFinderFrame() => _cameraService.GrabViewFinderFrame(); public (FrameBuffer, StreamConfiguration)? GrabViewFinderFrame() => _cameraService.GrabViewFinderFrame();
public void ReturnViewFinderFrame(FrameBuffer frameBuffer) => _cameraService.ReturnViewFinderFrame(frameBuffer); public void ReturnViewFinderFrame(FrameBuffer frameBuffer) => _cameraService.ReturnViewFinderFrame(frameBuffer);
+10 -8
View File
@@ -391,8 +391,10 @@ public partial class CameraView : OpenGlControlBase
try try
{ {
(LibCameraSharp.FrameBuffer, LibCameraSharp.StreamConfiguration)? frame = null; (LibCameraSharp.FrameBuffer, LibCameraSharp.StreamConfiguration)? frame = null;
while (frame is null) while (frame is null && (DataContext as CameraViewModel)?.CanGrabViewFinderFrame() == true)
frame = (DataContext as CameraViewModel)?.GrabViewFinderFrame(); frame = (DataContext as CameraViewModel)?.GrabViewFinderFrame();
if (frame is null)
return;
var (frameBuffer, streamConfiguration) = frame.Value; var (frameBuffer, streamConfiguration) = frame.Value;
// 0. Проверяем что полученый фрейм ожидаемый праильный формат // 0. Проверяем что полученый фрейм ожидаемый праильный формат
@@ -429,12 +431,12 @@ public partial class CameraView : OpenGlControlBase
return; return;
} }
var plane = frameBuffer.Planes.First(); var plane = frameBuffer.Planes.First();
if (plane.Length != width * height * 4) // if (plane.Length != width * height * 4)
{ // {
_logger.LogError("Cannot render frame, plane validation error, expected plane size 0x{:X}, got 0x{:X}", width * height * 4, plane.Length); // _logger.LogError("Cannot render frame, plane validation error, expected plane size 0x{:X}, got 0x{:X}", width * height * 4, plane.Length);
(DataContext as CameraViewModel)?.ReturnViewFinderFrame(frameBuffer); // (DataContext as CameraViewModel)?.ReturnViewFinderFrame(frameBuffer);
return; // return;
} // }
var fd = plane.Fd.Get(); var fd = plane.Fd.Get();
if (fd <= 0) if (fd <= 0)
{ {
@@ -468,7 +470,7 @@ public partial class CameraView : OpenGlControlBase
OpenGLContext.EGL_LINUX_DRM_FOURCC_EXT, unchecked((int)OpenGLContext.DRM_FORMAT_BGRX8888), OpenGLContext.EGL_LINUX_DRM_FOURCC_EXT, unchecked((int)OpenGLContext.DRM_FORMAT_BGRX8888),
OpenGLContext.EGL_DMA_BUF_PLANE0_FD_EXT, fd, OpenGLContext.EGL_DMA_BUF_PLANE0_FD_EXT, fd,
OpenGLContext.EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, OpenGLContext.EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
OpenGLContext.EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)(width * 4), OpenGLContext.EGL_DMA_BUF_PLANE0_PITCH_EXT, (int)(plane.Length / height),
EglConsts.EGL_NONE EglConsts.EGL_NONE
}; };
image = _openGLContext.EglCreateImageKHR.Invoke(_openGLContext.EglDisplay, IntPtr.Zero, OpenGLContext.EGL_LINUX_DMA_BUF_EXT, IntPtr.Zero, attribs); image = _openGLContext.EglCreateImageKHR.Invoke(_openGLContext.EglDisplay, IntPtr.Zero, OpenGLContext.EGL_LINUX_DMA_BUF_EXT, IntPtr.Zero, attribs);