Capture PS3 Eye pixels through direct driver

This commit is contained in:
Meta Vens 2026-05-27 14:33:36 +01:00
commit 58e7af8a76
11 changed files with 593 additions and 1 deletions

View file

@ -15,6 +15,62 @@
"pixelFormat": "YUY2",
"minimumFramesPerSecond": 100.0,
"queueDepth": 8
},
{
"sourceId": "ps3-eye-0",
"displayName": "PS3 Eye 0",
"kind": "Video",
"origin": "LocalDevice",
"enabled": true,
"adapter": "ps3eye",
"command": "../native/camera_capture/build/Release/mimir_ps3eye_capture.dll",
"cameraIndex": 0,
"width": 320,
"height": 240,
"framesPerSecond": 187
},
{
"sourceId": "ps3-eye-1",
"displayName": "PS3 Eye 1",
"kind": "Video",
"origin": "LocalDevice",
"enabled": true,
"adapter": "ps3eye",
"command": "../native/camera_capture/build/Release/mimir_ps3eye_capture.dll",
"cameraIndex": 1,
"width": 320,
"height": 240,
"framesPerSecond": 187
},
{
"sourceId": "kiyo-pro-yuy2",
"displayName": "Kiyo Pro YUY2",
"kind": "Video",
"origin": "LocalDevice",
"enabled": true,
"adapter": "ks-camera",
"command": "../native/camera_capture/build/Release/mimir_camera_capture.dll",
"pathNeedle": "vid_1532&pid_0e05&mi_00",
"width": 1920,
"height": 1080,
"pixelFormat": "YUY2",
"minimumFramesPerSecond": 25.0,
"queueDepth": 8
},
{
"sourceId": "kiyo-yuy2",
"displayName": "Kiyo YUY2",
"kind": "Video",
"origin": "LocalDevice",
"enabled": true,
"adapter": "ks-camera",
"command": "../native/camera_capture/build/Release/mimir_camera_capture.dll",
"pathNeedle": "vid_1532&pid_0e03&mi_00",
"width": 640,
"height": 480,
"pixelFormat": "YUY2",
"minimumFramesPerSecond": 25.0,
"queueDepth": 8
}
]
}

View file

@ -208,6 +208,13 @@ a named invariant that the native runtime cannot protect yet.
and lets `MimirVideoCaptureDriverSource` upload those raw frames into
Fensalir texture leases. MJPG/H264 remain outside this lane; they need a
device/GPU decode producer rather than a CPU convenience detour.
PS3 Eyes use the sibling `MimirPs3EyeVideoCaptureDriver` backed by
`native/camera_capture/mimir_ps3eye_capture.dll`; it opens the existing
WinUSB/libusb PS3EYEDriver path in process and emits Bayer8 frames through the
same source/upload/copy-count machinery.
Current direct-driver smokes prove real-frame upload for LeapUVC 640x240
YUY2, both PS3 Eyes at 320x240 Bayer8, Kiyo Pro 1920x1080 YUY2, and regular
Kiyo 640x480 YUY2. Regular Kiyo 1280x720 YUY2 did not open.
Fensalir can still resolve imported shared `Texture2D` resources by native
D3D12 handle and accepts Mimir video format names.
`Mimir.BufferSmoke --fensalir-camera-observation-smoke` verifies the split,

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)
project(mimir_camera_capture LANGUAGES CXX)
project(mimir_camera_capture LANGUAGES C CXX)
add_library(mimir_camera_capture SHARED
mimir_ks_camera_capture.cpp)
@ -12,3 +12,45 @@ target_compile_definitions(mimir_camera_capture PRIVATE
target_link_libraries(mimir_camera_capture PRIVATE
setupapi
ksuser)
set(PSMOVEAPI_ROOT "$ENV{TEMP}/mimir-driver-probes/psmoveapi" CACHE PATH "Root of a psmoveapi checkout with external/libusb-1.0 and external/PS3EYEDriver.")
set(LIBUSB_ROOT "${PSMOVEAPI_ROOT}/external/libusb-1.0" CACHE PATH "libusb source root.")
set(PS3EYE_ROOT "${PSMOVEAPI_ROOT}/external/PS3EYEDriver" CACHE PATH "PS3EYEDriver source root.")
add_library(mimir_libusb_static STATIC
${LIBUSB_ROOT}/libusb/core.c
${LIBUSB_ROOT}/libusb/descriptor.c
${LIBUSB_ROOT}/libusb/hotplug.c
${LIBUSB_ROOT}/libusb/io.c
${LIBUSB_ROOT}/libusb/strerror.c
${LIBUSB_ROOT}/libusb/sync.c
${LIBUSB_ROOT}/libusb/os/events_windows.c
${LIBUSB_ROOT}/libusb/os/threads_windows.c
${LIBUSB_ROOT}/libusb/os/windows_common.c
${LIBUSB_ROOT}/libusb/os/windows_usbdk.c
${LIBUSB_ROOT}/libusb/os/windows_winusb.c)
target_include_directories(mimir_libusb_static PUBLIC
${LIBUSB_ROOT}/libusb
${LIBUSB_ROOT}/libusb/os
${LIBUSB_ROOT}/msvc)
target_compile_definitions(mimir_libusb_static PRIVATE
_CRT_SECURE_NO_WARNINGS
_WIN32_WINNT=0x0601)
target_link_libraries(mimir_libusb_static PRIVATE setupapi ole32 advapi32)
add_library(mimir_ps3eye_vendor STATIC
${PS3EYE_ROOT}/src/ps3eye.cpp
${PS3EYE_ROOT}/src/ps3eye_capi.cpp)
target_include_directories(mimir_ps3eye_vendor PUBLIC
${PS3EYE_ROOT}/src
${LIBUSB_ROOT}/libusb)
target_link_libraries(mimir_ps3eye_vendor PUBLIC mimir_libusb_static)
add_library(mimir_ps3eye_capture SHARED
mimir_ps3eye_capture.cpp)
target_compile_features(mimir_ps3eye_capture PRIVATE cxx_std_20)
target_compile_definitions(mimir_ps3eye_capture PRIVATE
WIN32_LEAN_AND_MEAN
NOMINMAX)
target_include_directories(mimir_ps3eye_capture PRIVATE ${PS3EYE_ROOT}/src)
target_link_libraries(mimir_ps3eye_capture PRIVATE mimir_ps3eye_vendor)

View file

@ -0,0 +1,215 @@
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstdint>
#include <cstring>
#include <deque>
#include <mutex>
#include <thread>
#include <vector>
#include "ps3eye_capi.h"
namespace
{
struct MimirPs3EyeFrame
{
std::vector<unsigned char> data;
std::int64_t timestampNs = 0;
std::uint64_t sequence = 0;
};
struct MimirPs3EyeCapture
{
ps3eye_t* eye = nullptr;
std::thread worker;
std::mutex mutex;
std::condition_variable condition;
std::deque<MimirPs3EyeFrame> queue;
std::atomic<bool> running = false;
int cameraIndex = 0;
int width = 0;
int height = 0;
int fps = 0;
std::uint64_t sequence = 0;
};
std::mutex g_ps3eye_mutex;
int g_ps3eye_users = 0;
long long nowNs()
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
void retainPs3Eye()
{
std::lock_guard<std::mutex> lock(g_ps3eye_mutex);
if (g_ps3eye_users++ == 0)
{
ps3eye_init();
}
}
void releasePs3Eye()
{
std::lock_guard<std::mutex> lock(g_ps3eye_mutex);
if (g_ps3eye_users > 0 && --g_ps3eye_users == 0)
{
ps3eye_uninit();
}
}
void pushFrame(MimirPs3EyeCapture& capture, const std::vector<unsigned char>& source)
{
MimirPs3EyeFrame frame;
frame.data = source;
frame.timestampNs = nowNs();
frame.sequence = capture.sequence++;
{
std::lock_guard<std::mutex> lock(capture.mutex);
capture.queue.push_back(std::move(frame));
while (capture.queue.size() > 32)
{
capture.queue.pop_front();
}
}
capture.condition.notify_one();
}
void captureThread(MimirPs3EyeCapture* capture)
{
std::vector<unsigned char> frame(static_cast<std::size_t>(capture->width) * static_cast<std::size_t>(capture->height));
while (capture->running.load(std::memory_order_acquire))
{
ps3eye_grab_frame(capture->eye, frame.data());
pushFrame(*capture, frame);
}
}
}
extern "C"
{
__declspec(dllexport) MimirPs3EyeCapture* mimir_ps3eye_create(int cameraIndex, int width, int height, int fps)
{
if (cameraIndex < 0 || width <= 0 || height <= 0 || fps <= 0)
{
return nullptr;
}
retainPs3Eye();
if (cameraIndex >= ps3eye_count_connected())
{
releasePs3Eye();
return nullptr;
}
auto* capture = new MimirPs3EyeCapture();
capture->cameraIndex = cameraIndex;
capture->width = width;
capture->height = height;
capture->fps = fps;
capture->eye = ps3eye_open(cameraIndex, width, height, fps, PS3EYE_FORMAT_BAYER);
if (!capture->eye)
{
delete capture;
releasePs3Eye();
return nullptr;
}
return capture;
}
__declspec(dllexport) int mimir_ps3eye_start(MimirPs3EyeCapture* capture)
{
if (!capture || capture->running.exchange(true))
{
return 0;
}
capture->worker = std::thread(captureThread, capture);
return 1;
}
__declspec(dllexport) int mimir_ps3eye_read(
MimirPs3EyeCapture* capture,
int* width,
int* height,
int* stride,
std::int64_t* timestampNs,
std::uint64_t* sequence,
unsigned char* destination,
int destinationBytes)
{
if (!capture || !destination || destinationBytes <= 0)
{
return 0;
}
MimirPs3EyeFrame frame;
{
std::lock_guard<std::mutex> lock(capture->mutex);
if (capture->queue.empty())
{
return 0;
}
frame = std::move(capture->queue.front());
capture->queue.pop_front();
}
if (frame.data.size() > static_cast<std::size_t>(destinationBytes))
{
return -static_cast<int>(frame.data.size());
}
std::memcpy(destination, frame.data.data(), frame.data.size());
if (width)
{
*width = capture->width;
}
if (height)
{
*height = capture->height;
}
if (stride)
{
*stride = capture->width;
}
if (timestampNs)
{
*timestampNs = frame.timestampNs;
}
if (sequence)
{
*sequence = frame.sequence;
}
return static_cast<int>(frame.data.size());
}
__declspec(dllexport) void mimir_ps3eye_destroy(MimirPs3EyeCapture* capture)
{
if (!capture)
{
return;
}
capture->running.store(false, std::memory_order_release);
if (capture->worker.joinable())
{
capture->worker.join();
}
if (capture->eye)
{
ps3eye_close(capture->eye);
}
delete capture;
releasePs3Eye();
}
}

View file

@ -349,6 +349,12 @@ is the first production-shaped KS/UVC driver: it opens a Kernel Streaming pin
in process, queues uncompressed UVC frames, and feeds the same
`IMimirVideoCaptureDriver` source path. MJPG/H264 still need a device/GPU
decode producer; the KS driver rejects compressed capture formats.
`MimirPs3EyeVideoCaptureDriver` plus
`native/camera_capture/mimir_ps3eye_capture.dll` does the same for PS3 Eye raw
WinUSB/libusb capture, emitting Bayer8 frames through the common upload lane.
Driver smokes have pulled and uploaded real frames for LeapUVC 640x240 YUY2,
both PS3 Eyes at 320x240 Bayer8, Kiyo Pro 1920x1080 YUY2, and regular Kiyo
640x480 YUY2. Regular Kiyo 1280x720 YUY2 did not open.
Metadata-only cadence frames do not create camera surface intents or fake
payload requests. The old direct `AquariumGpuSensorFrame` bridge proof has been
removed from Mimir's active proof path. Camera image claims currently defer

View file

@ -58,6 +58,11 @@ Get-Content .\state\evidence.jsonl -Tail 8
The first direct KS/UVC driver is `MimirKsVideoCaptureDriver` backed by
`native/camera_capture/mimir_camera_capture.dll`; the example config
`config/mimir-runtime.ks-camera.example.json` targets LeapUVC 640x240 YUY2.
The same native folder also has `mimir_ps3eye_capture.dll` and
`MimirPs3EyeVideoCaptureDriver` for PS3 Eye Bayer8 frames through WinUSB/libusb.
Fresh driver smokes pulled and uploaded real frames for LeapUVC 640x240 YUY2,
both PS3 Eyes at 320x240 Bayer8, Kiyo Pro 1920x1080 YUY2, and regular Kiyo
640x480 YUY2. Regular Kiyo 1280x720 YUY2 did not open.
Compressed MJPG/H264 still need a device/GPU decode producer, not this raw
upload lane.
Foreign shared texture handles are import edges, not the primary hot path.

View file

@ -118,6 +118,11 @@ if (args.Any(arg => string.Equals(arg, "--ks-camera-driver-smoke", StringCompari
return RunKsCameraDriverSmoke(args);
}
if (args.Any(arg => string.Equals(arg, "--ps3eye-driver-smoke", StringComparison.OrdinalIgnoreCase)))
{
return RunPs3EyeDriverSmoke(args);
}
if (args.Any(arg => string.Equals(arg, "--mimir-spectrum-upload-smoke", StringComparison.OrdinalIgnoreCase)))
{
return RunMimirSpectrumUploadSmoke();
@ -871,6 +876,57 @@ static int RunKsCameraDriverSmoke(string[] args)
return 1;
}
static int RunPs3EyeDriverSmoke(string[] args)
{
var library = ParseStringOption(args, "--native-library", Path.GetFullPath("native/camera_capture/build/Release/mimir_ps3eye_capture.dll"));
var sourceId = ParseStringOption(args, "--source-id", "ps3-eye-0");
var cameraIndex = ParseIntOption(args, "--camera-index", 0);
var width = ParseIntOption(args, "--width", 320);
var height = ParseIntOption(args, "--height", 240);
var fps = ParseIntOption(args, "--fps", 187);
var timeoutMs = ParseIntOption(args, "--timeout-ms", 3000);
var broker = new FakeFieldResourceBroker();
using var driver = new MimirPs3EyeVideoCaptureDriver(new MimirPs3EyeVideoCaptureDriverOptions(
library,
sourceId,
cameraIndex,
width,
height,
fps));
using var source = new MimirVideoCaptureDriverSource(
new MimirStreamDescriptor(sourceId, MimirStreamKind.Video, MimirStreamOrigin.LocalDevice),
driver,
static () => StopwatchTicksToNs(Stopwatch.GetTimestamp()));
source.AttachTextureLeaseClient(new MimirFensalirTextureLeaseClient(broker));
var deadline = Stopwatch.GetTimestamp() + (long)(timeoutMs / 1000.0 * Stopwatch.Frequency);
while (Stopwatch.GetTimestamp() < deadline)
{
if (!source.TryRead(out var sample))
{
Thread.Sleep(1);
continue;
}
var frame = sample.VideoFrame;
Console.WriteLine(
$"ps3eye-driver-smoke source={sample.SourceId} camera={cameraIndex} {frame?.Width}x{frame?.Height} format={frame?.PixelFormat} liveBytes={sample.ByteLength} uploadedBytes={source.LastUploadedByteLength} uploaded={broker.UploadedResourceKey} copies={source.LastUploadedCopyCount}");
return frame != null &&
frame.Width == width &&
frame.Height == height &&
frame.PixelFormat == MimirVideoPixelFormat.Bayer8 &&
source.LastUploadedByteLength == width * height &&
source.LastUploadedCopyCount == 1 &&
broker.UploadedResourceKey == MimirFensalirTextureLeaseClient.ResourceKeyForSource(sourceId)
? 0
: 1;
}
Console.WriteLine($"ps3eye-driver-smoke timeout source={sourceId} camera={cameraIndex} {width}x{height}@{fps}");
return 1;
}
static long StopwatchTicksToNs(long ticks)
{
return checked((long)(ticks * (1_000_000_000.0 / Stopwatch.Frequency)));

View file

@ -0,0 +1,172 @@
using System.Runtime.InteropServices;
namespace Mimir.Runtime.Synchronization;
public sealed record MimirPs3EyeVideoCaptureDriverOptions(
string NativeLibraryPath,
string SourceId,
int CameraIndex,
int Width,
int Height,
int FramesPerSecond);
public sealed unsafe class MimirPs3EyeVideoCaptureDriver : IMimirVideoCaptureDriver, IMimirFensalirTextureLeaseReceiver
{
private readonly Native native;
private readonly nint capture;
private readonly byte[] scratch;
private readonly string sourceId;
private MimirFensalirTextureLeaseClient? textureLeaseClient;
private string resourceKey = "";
private bool disposed;
public MimirPs3EyeVideoCaptureDriver(MimirPs3EyeVideoCaptureDriverOptions options)
{
if (string.IsNullOrWhiteSpace(options.NativeLibraryPath))
{
throw new ArgumentException("PS3 Eye capture requires a native library path.", nameof(options));
}
if (string.IsNullOrWhiteSpace(options.SourceId))
{
throw new ArgumentException("PS3 Eye capture requires a source id.", nameof(options));
}
native = Native.Load(options.NativeLibraryPath);
sourceId = options.SourceId;
capture = native.Create(options.CameraIndex, options.Width, options.Height, options.FramesPerSecond);
if (capture == 0)
{
throw new InvalidOperationException($"PS3 Eye capture could not open camera {options.CameraIndex} at {options.Width}x{options.Height}@{options.FramesPerSecond}.");
}
scratch = new byte[checked(options.Width * options.Height)];
if (!native.Start(capture))
{
throw new InvalidOperationException("PS3 Eye capture could not start.");
}
}
public string DriverName => "ps3eye-winusb-direct";
public void AttachTextureLeaseClient(MimirFensalirTextureLeaseClient? client)
{
textureLeaseClient = client;
resourceKey = "";
}
public bool TryCapture(out MimirVideoFrameDescriptor frame, out ReadOnlyMemory<byte> data)
{
fixed (byte* destination = scratch)
{
var byteLength = native.Read(
capture,
out var width,
out var height,
out var stride,
out var timestampNs,
out var sequence,
destination,
scratch.Length);
if (byteLength <= 0)
{
frame = default!;
data = default;
return false;
}
EnsureLease(width, height, stride, timestampNs, sequence);
frame = new MimirVideoFrameDescriptor(
width,
height,
MimirVideoPixelFormat.Bayer8,
stride,
timestampNs,
ResourceKey: resourceKey,
ProducerFenceValue: sequence);
data = scratch.AsMemory(0, byteLength).ToArray();
return true;
}
}
public void Dispose()
{
if (disposed)
{
return;
}
disposed = true;
native.Destroy(capture);
native.Dispose();
}
private void EnsureLease(int width, int height, int stride, long timestampNs, ulong version)
{
if (!string.IsNullOrWhiteSpace(resourceKey) || textureLeaseClient == null)
{
return;
}
if (textureLeaseClient.TryLeaseTexture2D(new MimirFensalirTextureLeaseRequest(
sourceId,
width,
height,
MimirVideoPixelFormat.Bayer8,
stride,
timestampNs,
version), out var lease))
{
resourceKey = lease.Frame.ResourceKey;
}
}
private sealed class Native : IDisposable
{
private readonly nint library;
private readonly delegate* unmanaged[Stdcall]<int, int, int, int, nint> create;
private readonly delegate* unmanaged[Stdcall]<nint, int> start;
private readonly delegate* unmanaged[Stdcall]<nint, out int, out int, out int, out long, out ulong, byte*, int, int> read;
private readonly delegate* unmanaged[Stdcall]<nint, void> destroy;
private Native(nint library)
{
this.library = library;
create = (delegate* unmanaged[Stdcall]<int, int, int, int, nint>)NativeLibrary.GetExport(library, "mimir_ps3eye_create");
start = (delegate* unmanaged[Stdcall]<nint, int>)NativeLibrary.GetExport(library, "mimir_ps3eye_start");
read = (delegate* unmanaged[Stdcall]<nint, out int, out int, out int, out long, out ulong, byte*, int, int>)NativeLibrary.GetExport(library, "mimir_ps3eye_read");
destroy = (delegate* unmanaged[Stdcall]<nint, void>)NativeLibrary.GetExport(library, "mimir_ps3eye_destroy");
}
public static Native Load(string path) => new(NativeLibrary.Load(path));
public nint Create(int cameraIndex, int width, int height, int framesPerSecond) =>
create(cameraIndex, width, height, framesPerSecond);
public bool Start(nint capture) => start(capture) != 0;
public int Read(
nint capture,
out int width,
out int height,
out int stride,
out long timestampNs,
out ulong sequence,
byte* destination,
int destinationBytes) =>
read(capture, out width, out height, out stride, out timestampNs, out sequence, destination, destinationBytes);
public void Destroy(nint capture)
{
if (capture != 0)
{
destroy(capture);
}
}
public void Dispose()
{
NativeLibrary.Free(library);
}
}
}

View file

@ -172,6 +172,26 @@ public sealed class MimirRuntimeConfiguration
static () => StopwatchTicksToNs(System.Diagnostics.Stopwatch.GetTimestamp())));
}
if (string.Equals(stream.Adapter, "ps3eye", StringComparison.OrdinalIgnoreCase)
|| string.Equals(stream.Adapter, "ps3eye-direct", StringComparison.OrdinalIgnoreCase))
{
if (!OperatingSystem.IsWindows())
{
throw new PlatformNotSupportedException("The Mimir PS3 Eye source requires Windows.");
}
return new MimirStreamSourceFactory(descriptor, () => new MimirVideoCaptureDriverSource(
descriptor,
new MimirPs3EyeVideoCaptureDriver(new MimirPs3EyeVideoCaptureDriverOptions(
ResolveCommand(stream.Command, configDirectory),
stream.SourceId,
stream.CameraIndex,
stream.Width,
stream.Height,
stream.FramesPerSecond)),
static () => StopwatchTicksToNs(System.Diagnostics.Stopwatch.GetTimestamp())));
}
if (string.Equals(stream.Adapter, "frame-events", StringComparison.OrdinalIgnoreCase)
|| string.Equals(stream.Adapter, "json-lines", StringComparison.OrdinalIgnoreCase))
{
@ -352,6 +372,10 @@ public sealed class MimirStreamConfig
public int QueueDepth { get; set; } = 8;
public int CameraIndex { get; set; }
public int FramesPerSecond { get; set; }
public string DisplayNameForSource(string sourceId)
{
if (SourceLabels.TryGetValue(sourceId, out var label) && !string.IsNullOrWhiteSpace(label))

View file

@ -179,3 +179,4 @@
{"ts": "2026-05-27T14:07:32+01:00", "type": "decision", "status": "ok", "note": "Raw single-plane CPU-origin camera frames now have an explicit Fensalir texture upload fallback. MimirVideoCaptureDriverSource uploads returned frame bytes into the leased Fensalir Texture2D through the broker, clears the live managed payload after successful upload, and increments MimirVideoFrameDescriptor.UnavoidableCopyCount. CPU NV12 upload is rejected until Fensalir has a real planar copy path; GPU/direct producers should bypass this lane and remain at zero copies."}
{"ts": "2026-05-27T14:21:26+01:00", "type": "decision", "status": "ok", "note": "Mimir now has a production-shaped in-process KS/UVC camera driver. native/camera_capture builds mimir_camera_capture.dll, which opens a Kernel Streaming capture pin and queues uncompressed UVC frames; MimirKsVideoCaptureDriver feeds those frames through IMimirVideoCaptureDriver so MimirVideoCaptureDriverSource can upload single-plane raw pixels into Fensalir texture leases and count the unavoidable copy. MJPG/H264 remain future device/GPU decode producer work."}
{"ts": "2026-05-27T14:25:26+01:00", "type": "verification", "status": "ok", "note": "The new in-process KS/UVC driver pulled a real LeapUVC 640x240 YUY2 frame through native/camera_capture, MimirKsVideoCaptureDriver, and MimirVideoCaptureDriverSource, then uploaded 307200 bytes into the Fensalir texture lease for mimir:resource:camera:leap-stereo-ir:texture2d with UnavoidableCopyCount=1. Managed live payload bytes were cleared after upload."}
{"ts": "2026-05-27T14:31:40+01:00", "type": "verification", "status": "ok", "note": "Direct camera driver smokes pulled and uploaded real camera pixels into Fensalir texture leases: LeapUVC 640x240 YUY2 uploaded 307200 bytes, PS3 Eye 0 and 1 each uploaded 320x240 Bayer8 / 76800 bytes, Kiyo Pro uploaded 1920x1080 YUY2 / 4147200 bytes, and regular Kiyo uploaded 640x480 YUY2 / 614400 bytes. All successful paths cleared live managed payload bytes after upload and reported UnavoidableCopyCount=1. Regular Kiyo 1280x720 YUY2 did not open; compressed MJPG/H264 still require a device/GPU decode producer."}

View file

@ -329,6 +329,14 @@ accepted_design:
in process, queues uncompressed UVC frames, and feeds the same driver source
and Fensalir lease upload path. MJPG/H264 remain future device/GPU decode
producer work.
`MimirPs3EyeVideoCaptureDriver` plus
`native/camera_capture/mimir_ps3eye_capture.dll` owns the equivalent raw
WinUSB/libusb PS3 Eye path and emits Bayer8 frames through the same upload
and copy-count machinery.
Fresh direct-driver smokes uploaded real frames for LeapUVC 640x240 YUY2,
PS3 Eye 0/1 320x240 Bayer8, Kiyo Pro 1920x1080 YUY2, and regular Kiyo
640x480 YUY2. Regular Kiyo 1280x720 YUY2 did not open; compressed
MJPG/H264 remain device/GPU decode producer work.
`Mimir.BufferSmoke --fensalir-camera-observation-smoke`
verifies the split, and `--fensalir-texture-lease-smoke` verifies the
engine-owned lease path. Camera image claims currently defer until Fensalir