--- id: mobile-camera-ar-patterns title: Mobile Camera / AR β€” AVFoundation / CameraX / ARKit category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [mobile, camera, ar, vibe-coding] tech_stack: { language: "Swift / Kotlin", applicable_to: ["iOS", "Android"] } applied_in: [] aliases: [AVCaptureSession, CameraX, ARKit, ARCore, RealityKit, Sceneform, AR foundation] --- # Mobile Camera / AR > Camera = native API. **AVFoundation (iOS), CameraX (Android), ARKit / ARCore for AR**. ## πŸ“– 핡심 κ°œλ… - Capture session. - Frame processing (real-time). - Permission. - AR (anchor, plane, light). ## πŸ’» μ½”λ“œ νŒ¨ν„΄ ### iOS Camera (AVFoundation) ```swift let session = AVCaptureSession() session.sessionPreset = .high guard let device = AVCaptureDevice.default(for: .video), let input = try? AVCaptureDeviceInput(device: device) else { return } session.addInput(input) let output = AVCapturePhotoOutput() session.addOutput(output) let preview = AVCaptureVideoPreviewLayer(session: session) preview.frame = view.bounds view.layer.addSublayer(preview) session.startRunning() // Capture output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self) ``` ### iOS frame processing ```swift let videoOutput = AVCaptureVideoDataOutput() videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: .userInitiated)) session.addOutput(videoOutput) func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } // λ§€ frame: // - Vision / Core ML inference. // - Filter / draw. } ``` ### iOS permission ```xml NSCameraUsageDescription Used to take photos. ``` ```swift AVCaptureDevice.requestAccess(for: .video) { granted in if granted { setupCamera() } } ``` ### Android CameraX ```kotlin val cameraProvider = ProcessCameraProvider.getInstance(context).get() val preview = Preview.Builder().build().also { it.setSurfaceProvider(viewFinder.surfaceProvider) } val imageCapture = ImageCapture.Builder().build() val analyzer = ImageAnalysis.Builder().build().also { it.setAnalyzer(executor) { imageProxy -> // λ§€ frame. imageProxy.close() } } cameraProvider.bindToLifecycle( lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageCapture, analyzer ) ``` β†’ Modern Android camera API. ### CameraX permission ```kotlin val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted -> if (granted) setupCamera() } launcher.launch(Manifest.permission.CAMERA) ``` ```xml ``` ### Photo capture ```swift // iOS output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self) func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { let data = photo.fileDataRepresentation() // Save / process. } ``` ```kotlin // Android imageCapture.takePicture( ContextCompat.getMainExecutor(context), object : ImageCapture.OnImageCapturedCallback() { override fun onCaptureSuccess(image: ImageProxy) { // Process. image.close() } } ) ``` ### Video record (iOS) ```swift let movieOutput = AVCaptureMovieFileOutput() session.addOutput(movieOutput) let url = FileManager.default.temporaryDirectory.appendingPathComponent('video.mov') movieOutput.startRecording(to: url, recordingDelegate: self) // Stop movieOutput.stopRecording() ``` ### Video record (Android) ```kotlin val recorder = Recorder.Builder().setQualitySelector(QualitySelector.from(Quality.HD)).build() val videoCapture = VideoCapture.withOutput(recorder) cameraProvider.bindToLifecycle(lifecycleOwner, CameraSelector.DEFAULT_BACK_CAMERA, videoCapture) val recording = videoCapture.output .prepareRecording(context, fileOutputOptions) .start(executor) { event -> // Status. } // Stop recording.stop() ``` ### iOS ARKit ```swift import ARKit import RealityKit let arView = ARView(frame: view.bounds) view.addSubview(arView) let config = ARWorldTrackingConfiguration() config.planeDetection = .horizontal arView.session.run(config) // Place 3D object on plane arView.session.delegate = self func session(_ session: ARSession, didAdd anchors: [ARAnchor]) { for anchor in anchors { if let plane = anchor as? ARPlaneAnchor { // Add 3D model. } } } ``` ### Android ARCore ```kotlin // Sceneform λ˜λŠ” ARCore SDK val session = Session(context) val config = Config(session) config.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL session.configure(config) // λ§€ frame. val frame = session.update() val planes = frame.getUpdatedTrackables(Plane::class.java) ``` β†’ Sceneform κ°€ deprecated. ARCore 직접. ### Cross-platform AR ``` - Unity AR Foundation: Unity μΉœν™”. - React Native: react-native-arkit / react-native-arcore. - Flutter: ar_flutter_plugin. β†’ Native κ°€ κ°€μž₯ κ°•λ ₯. Cross-platform κ°€ limit. ``` ### ML on frame ```swift // iOS Vision + Core ML let request = VNCoreMLRequest(model: model) { request, error in // Result. } let handler = VNImageRequestHandler(cmSampleBuffer: sampleBuffer) try? handler.perform([request]) ``` ```kotlin // Android ML Kit val detector = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) val image = InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees) detector.process(image).addOnSuccessListener { result -> // text. } ``` ### Performance ``` 60 FPS = 16.7 ms / frame. ML κ°€ λ„ˆλ¬΄ 느린 = drop frame. β†’ Background thread. Smaller model. Skip frame (every 2nd). ``` ### Use case ``` - Photo / video capture. - QR / barcode (ML Kit / Vision). - Document scan. - AR (place, measure). - Filter (Snapchat 식). - OCR. - Face detect (Bumble 식). ``` ### Camera permission UX ``` μ‚¬μš©μž κ°€ deny ν›„: - Settings κ°€ manual λ³€κ²½. - Re-prompt μ•ˆ 됨. β†’ "Why need" λͺ…ν™•. 첫 prompt 신쀑. ``` ### Battery / heat ``` Camera κ°€ 큰 battery + heat. - μ‚¬μš© μ•ˆ μ‹œ stop session. - λ§€ frame ML κ°€ ν•„μš” μ‹œλ§Œ. - Resolution κ°€ use case 따라. ``` ### iOS LiDAR (modern) ```swift let config = ARWorldTrackingConfiguration() config.sceneReconstruction = .mesh if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) { // iPhone Pro 이상. } ``` β†’ Depth + 3D mesh. ### Photo metadata ```swift photo.metadata // GPS, exif, ... ``` ```kotlin // CameraX κ°€ μžλ™ set EXIF. ``` β†’ Privacy 신쀑. ### Streaming (live broadcast) ``` - HLS / DASH 둜 stream. - WebRTC (real-time). β†’ 큰 bandwidth. ``` ### Filter / effect ```swift // Core Image let context = CIContext() let filter = CIFilter.gaussianBlur() filter.setValue(ciImage, forKey: kCIInputImageKey) let output = filter.outputImage ``` β†’ Real-time gauss / sharpen / etc. ### React Native ``` - react-native-vision-camera (modern). - expo-camera. - frame processor (worklet for ML). ``` ### Flutter ``` - camera plugin. - image_picker. - ML κ°€ 별 plugin. ``` ### Production tips ``` 1. Permission flow 신쀑. 2. Resolution = use case 따라. 3. Background thread (ML). 4. Battery / heat 인지. 5. Privacy (metadata strip). 6. Test κ°€ λ§€ device (low-end). ``` ### 함정 ``` - Permission deny: re-prompt μ•ˆ 됨. - High resolution = slow + 큰 file. - ML 이 main thread: jank. - Background = stop session. - AR κ°€ λͺ¨λ“  device μ•ˆ 됨 (LiDAR / GPU). ``` ## πŸ€” μ˜μ‚¬κ²°μ • κΈ°μ€€ | μž‘μ—… | μΆ”μ²œ | |---|---| | iOS native | AVFoundation | | Android native | CameraX | | AR iOS | ARKit + RealityKit | | AR Android | ARCore | | ML detect | Vision (iOS) / ML Kit (Android) | | Cross-platform | RN Vision Camera / Flutter camera | | Document scan | VisionKit (iOS) / ML Kit (Android) | ## ❌ μ•ˆν‹°νŒ¨ν„΄ - **Permission deny + no fallback**: bad UX. - **High res + ML: drop frame. - **Background + session running**: battery. - **Metadata κ°€ store**: privacy leak. - **AR κ°€ λ§€ device κ°€μ •**: μ•ˆ 됨. ## πŸ€– LLM ν™œμš© 힌트 - AVFoundation (iOS) / CameraX (Android) κ°€ modern. - ARKit / ARCore κ°€ native AR. - ML κ°€ Vision / ML Kit. - Resolution + permission + privacy. ## πŸ”— κ΄€λ ¨ λ¬Έμ„œ - [[Android_CameraX_Patterns]] - [[iOS_Audio_Video_AVKit]] - [[Android_ML_Kit_Health]]