Files
prosperon/tests/webcam.ce
2025-12-19 16:42:40 -06:00

263 lines
7.0 KiB
Plaintext

// Test webcam display
var draw2d
var graphics
var os = use('os');
var input = use('input')
var json = use('json')
var surface = use('sdl3/surface')
input.watch($self)
// Create SDL video actor
var video_actor = use('sdl3/video');
var camera = use('camera')
var window_id = null;
var renderer_id = null;
var cam_id = null;
var cam_obj = null;
var cam_approved = false;
var webcam_texture = null;
// Handle camera events
$receiver(e => {
if (e.type == 'camera_device_approved' && e.which == cam_id) {
log.console("Camera approved!");
cam_approved = true;
} else if (e.type == 'camera_device_denied' && e.which == cam_id) {
log.error("Camera access denied!");
$stop();
}
})
// Create window
send(video_actor, {
kind: "window",
op: "create",
data: {
title: "Webcam Test",
width: 800,
height: 600
}
}, function(response) {
if (response.error) {
log.error("Failed to create window:", response.error);
return;
}
window_id = response.id;
log.console("Created window with id:", window_id);
// Create renderer
send(video_actor, {
kind: "window",
op: "makeRenderer",
id: window_id
}, function(response) {
if (response.error) {
log.error("Failed to create renderer:", response.error);
return;
}
renderer_id = response.id;
log.console("Created renderer with id:", renderer_id);
// Configure draw2d and graphics
draw2d = use('draw2d', video_actor, renderer_id)
graphics = use('graphics', video_actor, renderer_id)
// List available cameras
var cameras = camera.list();
if (cameras.length == 0) {
log.error("No cameras found!");
log.console(cameras)
$stop();
return;
}
log.console("Found", cameras.length, "camera(s)");
// Open the first camera
cam_id = cameras[0];
var cam_name = camera.name(cam_id);
var cam_position = camera.position(cam_id);
log.console("Opening camera:", cam_name, "Position:", cam_position);
// Get supported formats and try to find a good one
var formats = camera.supported_formats(cam_id);
log.console("Camera supports", formats.length, "formats");
// Look for a 640x480 format with preferred colorspace
var preferred_format = null;
for (var i = 0; i < formats.length; i++) {
if (formats[i].width == 640 && formats[i].height == 480) {
preferred_format = formats[i];
// Prefer JPEG or sRGB colorspace if available
if (formats[i].colorspace == "jpeg" || formats[i].colorspace == "srgb") {
break;
}
}
}
if (!preferred_format && formats.length > 0) {
// Use first available format
preferred_format = formats[0];
}
preferred_format.framerate_numerator = 30
if (preferred_format) {
log.console("Using format:", preferred_format.width + "x" + preferred_format.height,
"FPS:", preferred_format.framerate_numerator + "/" + preferred_format.framerate_denominator,
"Format:", preferred_format.format,
"Colorspace:", preferred_format.colorspace);
cam_obj = camera.open(cam_id, preferred_format);
} else {
cam_obj = camera.open(cam_id);
}
if (!cam_obj) {
log.error("Failed to open camera!");
$stop();
return;
}
log.console("Camera driver:", cam_obj.get_driver());
// Get and display the actual format being used
var actual_format = cam_obj.get_format();
log.console("Actual camera format:");
log.console(" Resolution:", actual_format.width + "x" + actual_format.height);
log.console(" Format:", actual_format.format);
log.console(" Colorspace:", actual_format.colorspace);
log.console(" FPS:", actual_format.framerate_numerator + "/" + actual_format.framerate_denominator);
// Start capturing after a short delay to wait for approval
$delay(start_capturing, 0.5);
});
});
var captured = false
function start_capturing() {
if (!cam_approved) {
log.console("Waiting for camera approval...");
$delay(start_capturing, 0.1);
return;
}
var frame = 0;
var start_time = os.now();
function capture_and_draw() {
frame++;
var t = os.now() - start_time;
// Clear the screen with a dark background
send(video_actor, {
kind: "renderer",
id: renderer_id,
op: "set",
prop: "drawColor",
value: [0.1, 0.1, 0.15, 1]
});
send(video_actor, {
kind: "renderer",
id: renderer_id,
op: "clear"
});
// Clear draw2d commands
draw2d.clear();
// Capture frame from camera
var surface = cam_obj.capture()
if (surface) {
// Create texture from surface directly
send(video_actor, {
kind: "renderer",
id: renderer_id,
op: "loadTexture",
data: surface
}, function(tex_response) {
if (tex_response.id) {
// Destroy old texture if exists to avoid memory leak
if (webcam_texture) {
send(video_actor, {
kind: "texture",
id: webcam_texture,
op: "destroy"
});
}
webcam_texture = tex_response.id;
}
});
}
// Draw the webcam texture if we have one
if (webcam_texture) {
send(video_actor, {
kind: "renderer",
id: renderer_id,
op: "copyTexture",
data: {
texture_id: webcam_texture,
dest: {x: 50, y: 50, width: 640, height: 480}
}
});
} else {
// Draw placeholder text while waiting for first frame
// draw2d.text("Waiting for webcam...", {x: 200, y: 250, size: 20});
}
// Draw info
/*
draw2d.text("Camera: " + camera.name(cam_id), {x: 20, y: 20, size: 16});
draw2d.text("Position: " + camera.position(cam_id), {x: 20, y: 40, size: 16});
draw2d.text("Frame: " + frame, {x: 20, y: 60, size: 16});
*/
// Flush all commands to renderer
draw2d.flush();
// Present the frame
send(video_actor, {
kind: "renderer",
id: renderer_id,
op: "present"
});
// Schedule next frame (30 FPS for webcam)
if (frame < 300) { // Run for 10 seconds
$delay(capture_and_draw, 1/30);
} else {
log.console("Test completed - captured", frame, "frames");
// Clean up resources
if (webcam_texture) {
send(video_actor, {
kind: "texture",
id: webcam_texture,
op: "destroy"
});
}
// Note: Camera is automatically closed when the object is garbage collected
cam_obj = null;
$delay($stop, 0.5);
}
}
capture_and_draw();
}
$delay(_ => {
// Capture frame from camera
var surface = cam_obj.capture().convert("rgba8888", "srgb")
log.console('capturing!')
graphics.save_png("test.png", surface.width, surface.height, surface.pixels(),surface.pitch)
}, 3)
// Stop after 12 seconds if not already stopped
$delay($stop, 12);