feat: CameraView OpenGL render

This commit is contained in:
2026-02-03 20:09:29 +03:00
parent dc3c195c21
commit 99ebb79e0f
9 changed files with 513 additions and 702 deletions
+110 -71
View File
@@ -335,7 +335,7 @@ public class CameraService : IDisposable
{
cancellationToken.ThrowIfCancellationRequested();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var request = _camera.CreateRequest(0);
var result = request.AddBuffer(_viewFinderStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
@@ -384,7 +384,7 @@ public class CameraService : IDisposable
{
cancellationToken.ThrowIfCancellationRequested();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var request = _camera.CreateRequest(0);
var result = request.AddBuffer(_imageCaptureStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
@@ -515,7 +515,7 @@ public class CameraService : IDisposable
foreach (var buffer in buffers)
{
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
var request = _camera.CreateRequest(1);
var result = request.AddBuffer(_imageCaptureStream, buffer);
if (result < 0)
throw new Exception("Error while request create");
@@ -660,7 +660,12 @@ public class CameraService : IDisposable
return image;
}
public async Task<(FrameBuffer, StreamConfiguration)?> CaptureViewFinderBuffer(int bufferId, CancellationToken cancellationToken)
public List<FrameBuffer> ViewFinderBufferQueue { get; private set; } = new List<FrameBuffer>();
private System.Timers.Timer? _viewFinderTimer = null;
private System.Timers.ElapsedEventHandler? _viewFinderTimerElapsed = null;
private EventHandler<Camera.RequestCompletedEventArgs>? _viewFinderRequestComplitedHandler = null;
public void StartViewFinder()
{
if (_camera is null)
throw new InvalidOperationException("Camera not initialized");
@@ -670,79 +675,113 @@ public class CameraService : IDisposable
throw new InvalidOperationException("ViewFinderStreamConfiguration not initialized");
if (_viewFinderFrameBufferAllocator is null)
throw new InvalidOperationException("ViewFinderFrameBufferAllocator not initialized");
if (_viewFinderTimer is not null ||
_viewFinderTimerElapsed is not null ||
_viewFinderRequestComplitedHandler is not null)
throw new InvalidOperationException();
var effectiveCancellationToken = CancellationTokenSource
.CreateLinkedTokenSource(_disposeCts.Token, cancellationToken)
.Token;
var buffers = _viewFinderFrameBufferAllocator.Buffers(_viewFinderStream);
var buffer = buffers.Skip(bufferId % buffers.Count()).First();
var request = _camera.CreateRequest(checked((ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()));
int fd = LibC.memfd_create($"buffer_fence_{bufferId % buffers.Count()}", 0x0002U);
if (fd < 0)
_viewFinderTimer = new System.Timers.Timer()
{
throw new Exception($"memfd_create error: {Marshal.GetLastPInvokeError()} {Marshal.GetLastPInvokeErrorMessage()}");
}
Console.WriteLine($"Created fd {fd}");
var fence = new Fence(new UniqueFD(fd));
var result = request.AddBuffer(_viewFinderStream, buffer, fence);
if (result < 0)
Interval = 1000 / _configuration.ViewFinderFps,
AutoReset = true,
};
try
{
var fenceFd = buffer.ReleaseFence()?.Fd?.Release();
Console.WriteLine($"Request queue failed, closing fd {fenceFd}");
if (fenceFd is not null)
LibC.close(fenceFd.Value);
throw new Exception("Error while request create");
var buffers = _viewFinderFrameBufferAllocator.Buffers(_viewFinderStream);
lock (ViewFinderBufferQueue)
foreach (var buffer in buffers)
ViewFinderBufferQueue.Add(buffer);
void RequestCompleted(object? sender, Camera.RequestCompletedEventArgs ea)
{
if (ea.Request.Cookie != 2)
return;
var buffer = ea.Request.FindBuffer(_viewFinderStream);
// _logger.LogInformation("Complited request for frame buffer {}", buffer);
lock (ViewFinderBufferQueue)
ViewFinderBufferQueue.Add(buffer);
}
_viewFinderRequestComplitedHandler = RequestCompleted;
_camera.RequestCompleted += _viewFinderRequestComplitedHandler;
void TimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea)
{
try
{
FrameBuffer buffer;
lock (ViewFinderBufferQueue)
{
if (ViewFinderBufferQueue.Count() > 0 && (
ViewFinderBufferQueue.First().Request.Cookie != 2 ||
ViewFinderBufferQueue.First().Request.Status == Request.StatusEnum.RequestComplete ||
ViewFinderBufferQueue.First().Request.Status == Request.StatusEnum.RequestCancelled))
{
buffer = ViewFinderBufferQueue.First();
ViewFinderBufferQueue.Remove(buffer);
}
else
return;
}
var request = _camera.CreateRequest(2);
request.AddBuffer(_viewFinderStream, buffer);
_camera.QueueRequest(request);
// _logger.LogInformation("Queued request for frame buffer {}", buffer);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while ViewFinder tick");
}
}
_viewFinderTimerElapsed = TimerElapsed;
_viewFinderTimer.Elapsed += _viewFinderTimerElapsed;
_viewFinderTimer.Start();
_logger.LogInformation("ViewFinder started");
}
// _logger.LogDebug("Request(s) created");
effectiveCancellationToken.ThrowIfCancellationRequested();
// var cts = new TaskCompletionSource();
// void RequestCompleted(object? sender, Camera.RequestCompletedEventArgs e)
// {
// if (request.Handle != e.Request.Handle)
// return;
// var buffer = e.Request.FindBuffer(_viewFinderStream);
// if (buffer.Metadata.Status == FrameMetadata.StatusEnum.FrameError ||
// buffer.Metadata.Status == FrameMetadata.StatusEnum.FrameStartup)
// {
// // _logger.LogDebug("Request buffers status: {}", buffer.Metadata.Status);
// e.Request.Reuse(Request.ReuseFlagEnum.ReuseBuffers);
// var result = _camera.QueueRequest(e.Request);
// if (result >= 0)
// return;
// }
// // _logger.LogDebug("Request complited: \n\tStatus: {}\n\tCookie: {}\n\tSequence: {}\n\tHasPendingBuffers: {}", e.Request.Status, e.Request.Cookie, e.Request.Sequence, e.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();
// cts.SetResult();
// }
// _camera.RequestCompleted += RequestCompleted;
result = _camera.QueueRequest(request);
if (result < 0)
catch (Exception ex)
{
// _camera.RequestCompleted -= RequestCompleted;
throw new Exception("Error while request queue");
_logger.LogError(ex, "Error while ViewFinder start");
_viewFinderTimer.Stop();
_viewFinderTimer.Dispose();
_viewFinderTimer = null;
_viewFinderTimerElapsed = null;
_camera.RequestCompleted -= _viewFinderRequestComplitedHandler;
_viewFinderRequestComplitedHandler = null;
lock (ViewFinderBufferQueue)
ViewFinderBufferQueue.Clear();
}
// _logger.LogDebug("Request queued");
// await cts.Task.WaitAsync(effectiveCancellationToken);
// _camera.RequestCompleted -= RequestCompleted;
// _logger.LogDebug("Request complited");
return new(buffer, _viewFinderStreamConfiguration);
}
public void StopViewFinder()
{
_viewFinderTimer?.Stop();
_viewFinderTimer?.Dispose();
_viewFinderTimer = null;
_camera?.RequestCompleted -= _viewFinderRequestComplitedHandler;
_viewFinderRequestComplitedHandler = null;
lock (ViewFinderBufferQueue)
ViewFinderBufferQueue.Clear();
}
public (FrameBuffer, StreamConfiguration)? GrabViewFinderFrame()
{
FrameBuffer? buffer = null;
lock (ViewFinderBufferQueue)
if (ViewFinderBufferQueue.Count() > 0)
{
buffer = ViewFinderBufferQueue.LastOrDefault();
if (buffer is not null)
ViewFinderBufferQueue.Remove(buffer);
}
if (buffer is not null && _viewFinderStreamConfiguration is not null)
return (buffer, _viewFinderStreamConfiguration);
return null;
}
public void ReturnViewFinderFrame(FrameBuffer frameBuffer)
{
lock (ViewFinderBufferQueue)
ViewFinderBufferQueue.Insert(0, frameBuffer);
}
protected virtual void Dispose(bool disposing)