JARVIS supports multiple camera input sources with automatic fallback. The system prioritizes Meta Ray-Ban glasses when available, but seamlessly falls back to iPhone camera for testing and development.
Primary Input
Meta Ray-Ban GlassesHands-free POV capture via DAT SDK
Set up the AVCaptureSession with appropriate presets:
private func configureSession() { captureSession.beginConfiguration() captureSession.sessionPreset = .medium // Balance quality & performance // Add back camera input guard let camera = AVCaptureDevice.default( .builtInWideAngleCamera, for: .video, position: .back ) else { NSLog("[iPhoneCamera] Failed to access back camera") captureSession.commitConfiguration() return } guard let input = try? AVCaptureDeviceInput(device: camera) else { return } if captureSession.canAddInput(input) { captureSession.addInput(input) } captureSession.commitConfiguration()}
2
Add Video Output
Configure video output with proper pixel format:
// Add video outputvideoOutput.videoSettings = [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]videoOutput.setSampleBufferDelegate(self, queue: sessionQueue)videoOutput.alwaysDiscardsLateVideoFrames = trueif captureSession.canAddOutput(videoOutput) { captureSession.addOutput(videoOutput)}
Set alwaysDiscardsLateVideoFrames = true to prevent frame buffer buildup during processing.
3
Handle Frame Rotation
Force portrait orientation for consistent output:
// Force portrait-oriented frames from the sensorif let connection = videoOutput.connection(with: .video) { if connection.isVideoRotationAngleSupported(90) { connection.videoRotationAngle = 90 }}
4
Process Video Frames
Implement the AVCaptureVideoDataOutputSampleBufferDelegate:
extension IPhoneCameraManager: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput( _ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection ) { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } let ciImage = CIImage(cvPixelBuffer: pixelBuffer) guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return } let image = UIImage(cgImage: cgImage) onFrameCaptured?(image) }}
@router.post("/url", response_model=CaptureResponse)async def capture_url(body: UrlRequest) -> CaptureResponse: """Download an image from a URL and run it through the pipeline.""" pipeline = _get_pipeline() capture_id = f"cap_{uuid4().hex[:12]}" async with httpx.AsyncClient(timeout=30.0) as client: resp = await client.get(body.url) resp.raise_for_status() data = resp.content content_type = resp.headers.get("content-type", "image/jpeg") result = await pipeline.process( capture_id=capture_id, data=data, content_type=content_type, source=body.source, ) return CaptureResponse( capture_id=capture_id, status="processed" if result.success else "error", total_frames=result.total_frames, faces_detected=result.faces_detected, persons_created=list(result.persons_created) )
View implementation in source/backend/capture/webhook.py:95
let videoSettings: [String: Any] = [ kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA, kCVPixelBufferWidthKey as String: 1280, kCVPixelBufferHeightKey as String: 720]