feat: CameraService

Add stable optainitng of raw image from camera
This commit is contained in:
2026-01-15 14:58:11 +03:00
parent 717efe6781
commit ced7e294eb
8 changed files with 467 additions and 90 deletions
@@ -0,0 +1,7 @@
namespace GSS2.Core.Extentions;
static class EnumerableExtentions
{
public static IEnumerable<(int i, T value)> Enumerate<T>(this IEnumerable<T> values) => values.Select((value, i) => (i, value));
}
+2 -1
View File
@@ -18,9 +18,10 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
<PackageReference Include="OpenCvSharp4.runtime.linux-arm" Version="4.11.0.20250506" />
<PackageReference Include="System.Device.Gpio" Version="4.0.1" />
<PackageReference Include="Iot.Device.Bindings" Version="4.0.1" />
<PackageReference Include="LibCameraSharp" Version="0.5.2-20251214" />
<PackageReference Include="LibCameraSharp" Version="0.5.2-20260114" />
</ItemGroup>
<ItemGroup>
+266 -74
View File
@@ -2,6 +2,8 @@ using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
using LibCameraSharp;
using Stream = LibCameraSharp.Stream;
using System.Buffers;
using GSS2.Core.Extentions;
namespace GSS2.Core.Hardware;
@@ -28,7 +30,7 @@ public class CameraService : IDisposable
public static CameraServiceConfiguration Default => new CameraServiceConfiguration
{
CameraId = "",
FrameBufferCount = 5,
ViewFinderFrameBufferCount = 5,
ViewFinderWidth = 1920,
ViewFinderHeight = 1080,
ViewFinderFps = 30,
@@ -37,10 +39,11 @@ public class CameraService : IDisposable
};
public string CameraId { get; set; } = "";
public int FrameBufferCount { get; set; }
public int ViewFinderFrameBufferCount { get; set; }
public int ViewFinderWidth { get; set; }
public int ViewFinderHeight { get; set; }
public int ViewFinderFps { get; set; }
public int ImageCaptureFrameBufferCount { get; set; }
public int ImageCaptureWidth { get; set; }
public int ImageCaptureHeight { get; set; }
}
@@ -64,10 +67,13 @@ public class CameraService : IDisposable
private CameraManager? _cameraManager;
private Camera? _camera;
private CameraConfiguration? _cameraConfiguration;
private FrameBufferAllocator? _frameBufferAllocator;
private StreamConfiguration? _viewFinderStreamConfiguration;
private StreamConfiguration? _imageCaptureStreamConfiguration;
private FrameBufferAllocator? _viewFinderFrameBufferAllocator;
private FrameBufferAllocator? _imageCaptureFrameBufferAllocator;
private Stream? _viewFinderStream;
private Stream? _imageCaptureStream;
private List<MappedBuffer>? _mappedBuffers;
private List<MappedBuffer>? _imageCaptureMappedBuffers;
private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
public CameraService(ILogger<CameraService> logger, CameraServiceConfiguration? configuration = null, bool autoInitialize = false)
@@ -87,6 +93,14 @@ public class CameraService : IDisposable
}
_logger.LogDebug(@$"CameraService use following configuration:
CameraId: {_configuration.CameraId}
ViewFinderFrameBufferCount: {_configuration.ViewFinderFrameBufferCount}
ViewFinderWidth: {_configuration.ViewFinderWidth}
ViewFinderHeight: {_configuration.ViewFinderHeight}
ViewFinderFps: {_configuration.ViewFinderFps}
ImageCaptureFrameBufferCount: {_configuration.ImageCaptureFrameBufferCount}
ImageCaptureWidth: {_configuration.ImageCaptureWidth}
ImageCaptureHeight: {_configuration.ImageCaptureHeight}
");
if (autoInitialize)
@@ -115,9 +129,12 @@ public class CameraService : IDisposable
CameraSelect(effectiveCancellationToken);
CameraAcquire(effectiveCancellationToken);
CameraConfigure(effectiveCancellationToken);
FrameBuffersAllocate(effectiveCancellationToken);
ImageCaptureFrameBuffersMap(effectiveCancellationToken);
// ViewFinderFrameBuffersAllocate(effectiveCancellationToken);
ImageCaptureFrameBuffersAllocate(effectiveCancellationToken);
CameraStart(effectiveCancellationToken);
// ViewFinderFrameBuffersInitialize(effectiveCancellationToken);
ImageCaptureFrameBuffersInitialize(effectiveCancellationToken);
ImageCaptureFrameBuffersMap(effectiveCancellationToken);
}
catch (Exception e)
{
@@ -189,20 +206,42 @@ public class CameraService : IDisposable
_cameraConfiguration = _camera.GenerateConfiguration(
StreamRoleEnum.Viewfinder,
StreamRoleEnum.StillCapture);
StreamRoleEnum.Raw);
var viewFinderConfig = _cameraConfiguration.First();
viewFinderConfig.BufferCount = (uint)_configuration.FrameBufferCount;
viewFinderConfig.Size = new Size(
(uint)_configuration.ViewFinderWidth,
(uint)_configuration.ViewFinderHeight);
_viewFinderStreamConfiguration = _cameraConfiguration.First();
_viewFinderStreamConfiguration.BufferCount = checked((uint)_configuration.ViewFinderFrameBufferCount);
_viewFinderStreamConfiguration.Size = new Size(
checked((uint)_configuration.ViewFinderWidth),
checked((uint)_configuration.ViewFinderHeight)
);
_imageCaptureStreamConfiguration = _cameraConfiguration.Skip(1).First();
_imageCaptureStreamConfiguration.BufferCount = checked((uint)_configuration.ImageCaptureFrameBufferCount);
_imageCaptureStreamConfiguration.Size = new Size(
checked((uint)_configuration.ImageCaptureWidth),
checked((uint)_configuration.ImageCaptureHeight)
);
if (_cameraConfiguration.Validate() == CameraConfiguration.Status.Invalid)
{
_logger.LogCritical("Error while camera configuration validation");
throw new Exception("Error while camera configuration validation");
}
_logger.LogDebug(@$"ViewFinder stream configuration:
Size: {_viewFinderStreamConfiguration.Size.ToString(true)}
FrameSize: {_viewFinderStreamConfiguration.FrameSize}
Stride: {_viewFinderStreamConfiguration.Stride}
ColorSpace: {_viewFinderStreamConfiguration.ColorSpace?.ToString(true)}
PixelFormat: {_viewFinderStreamConfiguration.PixelFormat?.ToString(true)}");
_logger.LogDebug(@$"ImageCapture stream configuration:
Size: {_imageCaptureStreamConfiguration.Size.ToString(true)}
FrameSize: {_imageCaptureStreamConfiguration.FrameSize}
Stride: {_imageCaptureStreamConfiguration.Stride}
ColorSpace: {_imageCaptureStreamConfiguration.ColorSpace?.ToString(true)}
PixelFormat: {_imageCaptureStreamConfiguration.PixelFormat?.ToString(true)}");
cancellationToken.ThrowIfCancellationRequested();
int result = _camera.Configure(_cameraConfiguration);
@@ -212,8 +251,8 @@ public class CameraService : IDisposable
throw new Exception($"Error while camera configuration {result}");
}
_viewFinderStream = _cameraConfiguration.First().Stream();
_imageCaptureStream = _cameraConfiguration.Skip(1).First().Stream();
_viewFinderStream = _viewFinderStreamConfiguration.Stream();
_imageCaptureStream = _imageCaptureStreamConfiguration.Stream();
}
private void CameraStart(CancellationToken cancellationToken)
{
@@ -231,22 +270,20 @@ public class CameraService : IDisposable
_state |= State.CameraStarted;
}
private void FrameBuffersAllocate(CancellationToken cancellationToken)
private void ViewFinderFrameBuffersAllocate(CancellationToken cancellationToken)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
if (_viewFinderStream is null)
throw new InvalidOperationException("ViewFinderStream not initialized");
if (_imageCaptureStream is null)
throw new InvalidOperationException("ImageCaptureStream not initialized");
cancellationToken.ThrowIfCancellationRequested();
_frameBufferAllocator = new FrameBufferAllocator(_camera);
_viewFinderFrameBufferAllocator = new FrameBufferAllocator(_camera);
cancellationToken.ThrowIfCancellationRequested();
int result = _frameBufferAllocator.Allocate(_viewFinderStream);
int result = _viewFinderFrameBufferAllocator.Allocate(_viewFinderStream);
if (result < 0)
{
_logger.LogCritical("Error while view finder frame buffers allocation {Result}", result);
@@ -254,10 +291,21 @@ public class CameraService : IDisposable
}
_state |= State.ViewFinderFrameBufferAllocated;
}
private void ImageCaptureFrameBuffersAllocate(CancellationToken cancellationToken)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
if (_imageCaptureStream is null)
throw new InvalidOperationException("ImageCaptureStream not initialized");
cancellationToken.ThrowIfCancellationRequested();
result = _frameBufferAllocator.Allocate(_imageCaptureStream);
_imageCaptureFrameBufferAllocator = new FrameBufferAllocator(_camera);
cancellationToken.ThrowIfCancellationRequested();
int result = _imageCaptureFrameBufferAllocator.Allocate(_imageCaptureStream);
if (result < 0)
{
_logger.LogCritical("Error while image capture frame buffers allocation {Result}", result);
@@ -266,47 +314,137 @@ public class CameraService : IDisposable
_state |= State.ImageCaptureFrameBufferAllocated;
}
private void ViewFinderFrameBuffersInitialize(CancellationToken cancellationToken)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
if (_viewFinderStream is null)
throw new InvalidOperationException("ViewFinderStream not initialized");
if (_viewFinderFrameBufferAllocator is null)
throw new InvalidOperationException("ViewFinderFrameBufferAllocator not initialized");
cancellationToken.ThrowIfCancellationRequested();
var buffers = _viewFinderFrameBufferAllocator.Buffers(_viewFinderStream);
_logger.LogDebug("View finder frame buffers initialization");
foreach (var buffer in buffers)
{
cancellationToken.ThrowIfCancellationRequested();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var result = request.AddBuffer(_viewFinderStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
var cts = new TaskCompletionSource();
void RequestCompleted(object? sender, Camera.RequestCompletedEventArgs e)
{
if (e.Request.Handle == request.Handle)
cts.SetResult();
}
_camera.RequestCompleted += RequestCompleted;
result = _camera.QueueRequest(request);
if (result < 0)
{
_camera.RequestCompleted -= RequestCompleted;
throw new Exception("Error while request queue");
}
cts.Task.WaitAsync(cancellationToken).GetAwaiter().GetResult();
_camera.RequestCompleted -= RequestCompleted;
_logger.LogDebug("Request complited: \n\tResult: {} \n\tStatus: {}\n\tCookie: {}\n\tSequence: {}\n\tHasPendingBuffers: {}", result, request.Status, request.Cookie, request.Sequence, request.HasPendingBuffers);
_logger.LogDebug("Request buffer: \n\tStatus: {} \n\tCookie: {} \n\tSequence: {} \n\tTimestamp: {}", buffer.Metadata.Status, buffer.Cookie, buffer.Metadata.Sequence, buffer.Metadata.Timestamp);
}
}
private void ImageCaptureFrameBuffersInitialize(CancellationToken cancellationToken)
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
if (_imageCaptureStream is null)
throw new InvalidOperationException("ImageCaptureStream not initialized");
if (_imageCaptureFrameBufferAllocator is null)
throw new InvalidOperationException("ImageCaptureFrameBufferAllocator not initialized");
cancellationToken.ThrowIfCancellationRequested();
var buffers = _imageCaptureFrameBufferAllocator.Buffers(_imageCaptureStream);
_logger.LogDebug("Image capture frame buffers initialization");
foreach (var buffer in buffers)
{
cancellationToken.ThrowIfCancellationRequested();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var result = request.AddBuffer(_imageCaptureStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
var cts = new TaskCompletionSource();
void RequestCompleted(object? sender, Camera.RequestCompletedEventArgs e)
{
if (e.Request.Handle == request.Handle)
cts.SetResult();
}
_camera.RequestCompleted += RequestCompleted;
result = _camera.QueueRequest(request);
if (result < 0)
{
_camera.RequestCompleted -= RequestCompleted;
throw new Exception("Error while request queue");
}
cts.Task.WaitAsync(cancellationToken).GetAwaiter().GetResult();
_camera.RequestCompleted -= RequestCompleted;
_logger.LogDebug("Request complited: \n\tResult: {} \n\tStatus: {}\n\tCookie: {}\n\tSequence: {}\n\tHasPendingBuffers: {}", result, request.Status, request.Cookie, request.Sequence, request.HasPendingBuffers);
_logger.LogDebug("Request buffer: \n\tStatus: {} \n\tCookie: {} \n\tSequence: {} \n\tTimestamp: {}", buffer.Metadata.Status, buffer.Cookie, buffer.Metadata.Sequence, buffer.Metadata.Timestamp);
}
}
private void ImageCaptureFrameBuffersMap(CancellationToken cancellationToken)
{
if (_imageCaptureStream is null)
throw new InvalidOperationException("ImageCaptureStream not initialized");
if (_frameBufferAllocator is null)
throw new InvalidOperationException("FrameBufferAllocator not initialized");
if (_imageCaptureFrameBufferAllocator is null)
throw new InvalidOperationException("ImageCaptureFrameBufferAllocator not initialized");
cancellationToken.ThrowIfCancellationRequested();
_mappedBuffers = new List<MappedBuffer>();
_imageCaptureMappedBuffers = new List<MappedBuffer>();
var frameBuffers = _frameBufferAllocator.Buffers(_imageCaptureStream);
var frameBuffers = _imageCaptureFrameBufferAllocator.Buffers(_imageCaptureStream);
foreach (var buffer in frameBuffers)
{
cancellationToken.ThrowIfCancellationRequested();
var planesByFd = buffer.Planes.GroupBy(p => p.Fd);
var planesByFd = buffer.Planes.GroupBy(p => p.Fd.Get());
foreach (var fdGroup in planesByFd)
{
cancellationToken.ThrowIfCancellationRequested();
int fd = fdGroup.Key.Get();
int fd = fdGroup.Key;
ulong minOffset = fdGroup.Min(p => p.Offset);
ulong maxEnd = fdGroup.Max(p => p.Offset + p.Length);
ulong maxOffset = fdGroup.Max(p => p.Offset + p.Length);
ulong pageSize = (ulong)Environment.SystemPageSize;
ulong alignedOffset = minOffset & ~(pageSize - 1);
ulong alignedLength = maxEnd - alignedOffset;
ulong alignedLength = (maxOffset - alignedOffset + pageSize - 1) & ~(pageSize - 1);
IntPtr address = LibC.mmap(IntPtr.Zero, alignedLength, LibC.PROT_READ, LibC.MAP_SHARED, fd, alignedOffset);
if (address == new IntPtr(-1))
{
int error = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Error while buffers mapping, mmap failed for fd={fd}, error={error}");
throw new InvalidOperationException($"Error while buffers mapping, mmap failed for fd=0x{fd:X}, error={error}");
}
_mappedBuffers.Add(new MappedBuffer
_logger.LogDebug("Mapped buffer fd=0x{:X}, address=0x{:X}, length=0x{:X}", fd, address, alignedLength);
_imageCaptureMappedBuffers.Add(new MappedBuffer
{
Fd = fd,
Address = address,
@@ -325,10 +463,12 @@ public class CameraService : IDisposable
throw new InvalidOperationException("Camera not initialized");
if (_imageCaptureStream is null)
throw new InvalidOperationException("ImageCaptureStream not initialized");
if (_frameBufferAllocator is null)
throw new InvalidOperationException("FrameBufferAllocator not initialized");
if (_mappedBuffers is null)
throw new InvalidOperationException("Buffers not mapped");
if (_imageCaptureStreamConfiguration is null)
throw new InvalidOperationException("ImageCaptureStreamConfiguration not initialized");
if (_imageCaptureFrameBufferAllocator is null)
throw new InvalidOperationException("ImageCaptureFrameBufferAllocator not initialized");
if (_imageCaptureMappedBuffers is null)
throw new InvalidOperationException("ImageCaptureMappedBuffers not mapped");
var effectiveCancellationToken = CancellationTokenSource
.CreateLinkedTokenSource(_disposeCts.Token, cancellationToken)
@@ -336,15 +476,16 @@ public class CameraService : IDisposable
OpenCvSharp.Mat? image = null;
var fdsData = new Dictionary<int, byte[]>();
var planesData = new List<byte[]>();
try
{
effectiveCancellationToken.ThrowIfCancellationRequested();
var buffers = _frameBufferAllocator.Buffers(_imageCaptureStream);
var buffer = buffers.Where(b => b.Metadata.Status == FrameMetadata.StatusEnum.FrameStartup || b.Metadata.Status == FrameMetadata.StatusEnum.FrameSuccess).First();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var buffers = _imageCaptureFrameBufferAllocator.Buffers(_imageCaptureStream);
var buffer = buffers.First();
var result = request.AddBuffer(_imageCaptureStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
@@ -353,42 +494,85 @@ public class CameraService : IDisposable
var cts = new TaskCompletionSource();
void RequestCompleted(object? sender, Camera.RequestCompletedEventArgs e)
{
if (e.Request.Handle == request.Handle)
cts.SetResult();
};
if (e.Request.Handle != request.Handle)
return;
var bufferStatus = request.FindBuffer(_imageCaptureStream).Metadata.Status;
_logger.LogDebug("Request buffers status: {}", bufferStatus);
if (bufferStatus == FrameMetadata.StatusEnum.FrameError || bufferStatus == FrameMetadata.StatusEnum.FrameStartup)
{
request.Reuse(Request.ReuseFlagEnum.ReuseBuffers);
result = _camera.QueueRequest(request);
if (result >= 0)
return;
}
cts.SetResult();
}
_camera.RequestCompleted += RequestCompleted;
result = _camera.QueueRequest(request);
if (result < 0)
{
_camera.RequestCompleted -= RequestCompleted;
throw new Exception("Error while request queue");
}
await cts.Task.WaitAsync(effectiveCancellationToken);
_camera.RequestCompleted -= RequestCompleted;
var planesData = new List<Memory<byte>>();
buffer = request.FindBuffer(_imageCaptureStream);
_logger.LogDebug("Request complited: \n\tResult: {} \n\tStatus: {}\n\tCookie: {}\n\tSequence: {}\n\tHasPendingBuffers: {}", result, request.Status, request.Cookie, request.Sequence, request.HasPendingBuffers);
_logger.LogDebug("Request buffer: \n\tStatus: {} \n\tCookie: {} \n\tSequence: {} \n\tTimestamp: {}", buffer.Metadata.Status, buffer.Cookie, buffer.Metadata.Sequence, buffer.Metadata.Timestamp);
_logger.LogDebug("Request buffer planes: ");
foreach (var (i, plane) in buffer.Metadata.Planes.Enumerate())
_logger.LogDebug("Plane[{}]: bytes used: 0x{:X}", i, plane.BytesUsed);
foreach (var (i, plane) in buffer.Planes.Enumerate())
_logger.LogDebug("Plane[{}]: \n\tFd: 0x{:X} \n\tOffset: 0x{:X} \n\tLength: 0x{:X} \n\tKInvalidOffset: 0x{:X}", i, plane.Fd.Get(), plane.Offset, plane.Length, plane.KInvalidOffset);
effectiveCancellationToken.ThrowIfCancellationRequested();
foreach (var plane in buffer.Planes)
{
effectiveCancellationToken.ThrowIfCancellationRequested();
var fd = plane.Fd.Get();
if (fdsData.ContainsKey(fd))
continue;
var mappedBuffer = _imageCaptureMappedBuffers.Find(b => b.Fd == fd);
if (mappedBuffer is null)
continue;
var fdData = new byte[mappedBuffer.Length];
Marshal.Copy(mappedBuffer.Address, fdData, 0, checked((int)mappedBuffer.Length));
fdsData.Add(fd, fdData);
}
var pointer = GetPlanePointer(plane);
var data = new byte[plane.Length];
Marshal.Copy(pointer, data, 0, checked((int)plane.Length));
planesData.Add(new Memory<byte>(data));
foreach (var plane in buffer.Planes)
{
var fd = plane.Fd.Get();
if (!fdsData.ContainsKey(fd))
continue;
var fdData = fdsData[fd];
var planeData = fdData
.Skip(checked((int)plane.Offset))
.Take(checked((int)plane.Length))
.ToArray();
planesData.Add(planeData);
}
effectiveCancellationToken.ThrowIfCancellationRequested();
var config = _imageCaptureStream.Configuration();
var config = _imageCaptureStreamConfiguration;
var size = config.Size;
var fourcc = config.PixelFormat.Fourcc;
var modifier = config.PixelFormat.Modifier;
var primariesEnum = config.ColorSpace!.Primaries;
var transferFunctionEnum = config.ColorSpace.TransferFunction;
var ycbcrEncodingEnum = config.ColorSpace.YcbcrEncoding;
var rangeEnum = config.ColorSpace.Range;
var primariesEnum = config.ColorSpace?.Primaries;
var transferFunctionEnum = config.ColorSpace?.TransferFunction;
var ycbcrEncodingEnum = config.ColorSpace?.YcbcrEncoding;
var rangeEnum = config.ColorSpace?.Range;
var stride = config.Stride;
effectiveCancellationToken.ThrowIfCancellationRequested();
var decoder = FrameDecoderHelper.FindDecoder(fourcc, modifier, primariesEnum, transferFunctionEnum, ycbcrEncodingEnum, rangeEnum);
if (decoder is null)
throw new Exception($"Cannot find decoder for fourcc={fourcc} modifier={modifier} primariesEnum={primariesEnum} transferFunctionEnum={transferFunctionEnum} ycbcrEncodingEnum={ycbcrEncodingEnum} rangeEnum={rangeEnum}");
throw new Exception($"Cannot find decoder for fourcc=0x{fourcc:X}({new string(BitConverter.GetBytes(fourcc).Select(b => (char)b).ToArray())}) modifier=0x{modifier:X} primariesEnum={primariesEnum} transferFunctionEnum={transferFunctionEnum} ycbcrEncodingEnum={ycbcrEncodingEnum} rangeEnum={rangeEnum}");
effectiveCancellationToken.ThrowIfCancellationRequested();
@@ -396,9 +580,9 @@ public class CameraService : IDisposable
{
image = decoder.Decode(checked((int)size.Width), checked((int)size.Height), checked((int)stride), planesData);
}
catch (Exception e)
catch (Exception ex)
{
_logger.LogError(e, "Error while image decode");
_logger.LogError(ex, "Error while image decode");
throw;
}
}
@@ -406,17 +590,19 @@ public class CameraService : IDisposable
{
_logger.LogError(e, "Error while image capture");
}
finally
{
foreach (var key in fdsData.Keys)
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
fdsData[key] = null;
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
for (int i = 0; i < planesData.Count(); i++)
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
planesData[i] = null;
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
return image;
}
private IntPtr GetPlanePointer(FrameBuffer.Plane plane)
{
if (_mappedBuffers is null)
throw new InvalidOperationException("Buffers not mapped");
var mapped = _mappedBuffers.First(m => m.Fd == plane.Fd.Get());
ulong offsetInMapping = plane.Offset - mapped.AlignedOffset;
return IntPtr.Add(mapped.Address, checked((int)offsetInMapping));
}
protected virtual void Dispose(bool disposing)
{
@@ -435,20 +621,24 @@ public class CameraService : IDisposable
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private void Cleanup()
{
try
{
if (_state.HasFlag(State.ImageCaptureFrameBuffersMapped) && _mappedBuffers is not null)
if (_state.HasFlag(State.ImageCaptureFrameBuffersMapped) && _imageCaptureMappedBuffers is not null)
{
foreach (var buffer in _mappedBuffers)
foreach (var buffer in _imageCaptureMappedBuffers)
{
_logger.LogDebug("MUNMAP address=0x{:X}, length=0x{:X}", buffer.Address, buffer.Length);
LibC.munmap(buffer.Address, buffer.Length);
_mappedBuffers.Clear();
}
_imageCaptureMappedBuffers.Clear();
}
if (_state.HasFlag(State.ViewFinderFrameBufferAllocated) && _viewFinderStream is not null)
_frameBufferAllocator?.Free(_viewFinderStream);
_viewFinderFrameBufferAllocator?.Free(_viewFinderStream);
if (_state.HasFlag(State.ImageCaptureFrameBufferAllocated) && _imageCaptureStream is not null)
_frameBufferAllocator?.Free(_imageCaptureStream);
_imageCaptureFrameBufferAllocator?.Free(_imageCaptureStream);
if (_state.HasFlag(State.CameraStarted))
_camera?.Stop();
if (_state.HasFlag(State.CameraAcquired))
@@ -456,9 +646,10 @@ public class CameraService : IDisposable
if (_state.HasFlag(State.CameraManagerStarted))
_cameraManager?.Stop();
_viewFinderStream?.Dispose();
_imageCaptureStream?.Dispose();
_frameBufferAllocator?.Dispose();
// _viewFinderStream?.Dispose();
// _imageCaptureStream?.Dispose();
// _viewFinderFrameBufferAllocator?.Dispose();
// _imageCaptureFrameBufferAllocator?.Dispose();
_cameraManager?.Dispose();
}
catch (Exception e)
@@ -469,10 +660,11 @@ public class CameraService : IDisposable
{
_cameraManager = null;
_camera = null;
_frameBufferAllocator = null;
_viewFinderFrameBufferAllocator = null;
_imageCaptureFrameBufferAllocator = null;
_viewFinderStream = null;
_imageCaptureStream = null;
_mappedBuffers = null;
_imageCaptureMappedBuffers = null;
_state = State.NotInitialized;
}
}