// Test webcam display var draw2d var graphics var os = use('os'); var input = use('input') var json = use('json') var surface = use('sdl/surface') input.watch($_) // Create SDL video actor var video_actor = use('sdl/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(json.encode(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);