react-native-vision-camera

repository·main·Indexed 27 days ago

https://github.com/mrousavy/react-native-vision-camera

A high-performance camera library for React Native supporting photo/video capture, QR/barcode scanning, frame processors for AI, and GPU-accelerated resizing. The ecosystem includes specialized packages such as react-native-vision-camera-barcode-scanner, react-native-vision-camera-location, react-native-vision-camera-resizer, react-native-vision-camera-skia, and react-native-vision-camera-worklets.

Tokens
47K
Snippets
153
Records
228
Agent score
91%

What's inside react-native-vision-camera

  1. Understand Camera Outputs

    main

    A CameraOutput is the base class for all data streams a CameraDevice can provide through a CameraSession. VisionCamera provides five core output types:

    • CameraPhotoOutput: For capturing still photos.
    • CameraVideoOutput: For capturing video.
    • CameraFrameOutput: For accessing raw video frames.
    • CameraDepthFrameOutput: For accessing depth data.
    • CameraPreviewOutput: For displaying the camera preview.
  2. Understand NativeBuffer

    main

    A NativeBuffer is a JavaScript object that provides access to the underlying native GPU-backed buffer of a Frame. It is designed to allow interoperability with third-party native libraries without requiring additional native dependencies.

    A NativeBuffer consists of:

    • pointer: A bigint representing a pointer to a native C++ instance (with a +1 retain count).
    • release(): A function that must be called to release the buffer's reference.

    CRITICAL: You must call release() once you are finished using the NativeBuffer. Failure to do so will result in memory leaks and will stall the Camera pipeline.

  3. Understand Camera Device Types

    main

    Camera devices are categorized into Physical and Virtual types.

    Physical Device Types

    • 'ultra-wide-angle': A camera with an ultra-wide field of view (e.g., 0.5x).
    • 'wide-angle': A standard wide field of view (e.g., 1x).
    • 'telephoto': A narrow field of view for large zooms (e.g., 3x or 5x).

    Virtual Devices

    Virtual devices (like a 'triple' camera) consist of multiple physical devices. They allow for seamless switching between physical lenses to provide a smooth zoom experience.

  4. Create a Photo Output

    main

    The CameraPhotoOutput allows you to capture processed and RAW Photo objects. You can instantiate a photo output using three different patterns depending on your architecture:

    1. Using the <Camera /> component: Pass the output to the outputs prop.
    2. Using the useCamera hook: Pass the output to the outputs array in the hook configuration.
    3. Using CameraSession (Imperative): Create the output via VisionCamera.createPhotoOutput and configure it within a session.

    Refer to PhotoOutputOptions for configuration details.

    // Pattern 1: <Camera /> component
    function App() {
      const device = useCameraDevice('back')
      const photoOutput = usePhotoOutput({ /* options */ })
    
      return (
        <Camera
          style={StyleSheet.absoluteFill}
          isActive={true}
          device={device}
          outputs={[photoOutput]}
        />
      )
    }
    
    // Pattern 2: useCamera hook
    function App() {
      const device = useCameraDevice('back')
      const photoOutput = usePhotoOutput({ /* options */ })
    
      const camera = useCamera({
        isActive: true,
        device: device,
        outputs: [photoOutput],
      })
    }
    
    // Pattern 3: CameraSession (imperative)
    const session = await VisionCamera.createCameraSession(false)
    const photoOutput = VisionCamera.createPhotoOutput({ /* options */ })
    
    await session.configure([
      {
        input: 'back',
        outputs: [
          { output: photoOutput, mirrorMode: 'auto' }
        ],
        constraints: []
      }
    ], {})
    await session.start()
  5. Handle Frame orientation and mirroring

    main

    A Frame provides orientation and isMirrored metadata to describe its intended presentation relative to the output's configuration. The camera pipeline does not physically rotate or mirror buffers because it is computationally expensive; instead, consumers should apply rotation/mirroring logic themselves (e.g., using GPU-based matrix transformations in Skia).

    If you require buffers that are already correctly rotated and mirrored, enable enablePhysicalBufferRotation in your FrameOutputOptions. When this is enabled, orientation will always be 'up' and isMirrored will always be false.

  6. Handle CameraSession interruptions

    main

    A CameraSession can be interrupted by system events like incoming FaceTime calls or thermal throttling (see InterruptionReason). You can listen for these events using the following methods depending on your API style:

    // <Camera /> view
    <Camera
      isActive={true}
      device="back"
      onInterruptionStarted={(reason) => console.log(`Interrupted: ${reason}`)}
      onInterruptionEnded={() => console.log(`Interruption ended`)}
    />
    
    // useCamera() hook
    const camera = useCamera({
      isActive: true,
      device: 'back',
      onInterruptionStarted(reason) {
        console.log(`Interrupted: ${reason}`)
      },
      onInterruptionEnded() {
        console.log(`Interruption ended`)
      }
    })
    
    // CameraSession (imperative)
    session.addOnInterruptionStartedListener((reason) => {
      console.log(`Interrupted: ${reason}`)
    })
    session.addOnInterruptionEndedListener(() => {
      console.log(`Interruption ended`)
    })
  7. Create a CameraSession

    main

    A CameraSession connects camera inputs to outputs and manages the camera lifecycle. You can create a session using the <Camera /> view, the useCamera() hook, or by manually creating a CameraSession instance for imperative control.

    // 1. Using the <Camera /> view
    <Camera
      style={StyleSheet.absoluteFill}
      isActive={true}
      device="back"
    />
    
    // 2. Using the useCamera() hook
    const camera = useCamera({
      isActive: true,
      device: 'back',
    })
    
    // 3. Using CameraSession (imperative)
    const device = ...
    const isMultiCam = false
    const session = await VisionCamera.createCameraSession(isMultiCam)
    await session.configure([
      {
        input: device,
        outputs: [],
        constraints: []
      }
    ], {})
    await session.start()
  8. Prepare and use a Recorder

    main

    To capture a video, you must follow these steps:

    1. Prepare the Recorder: Use videoOutput.createRecorder() to create a Recorder instance.
    2. Start Recording: Call recorder.startRecording(onFinished, onError).
    3. Stop Recording: Call recorder.stopRecording() to end the session.

    Warning: Do not re-use a Recorder instance. To start a new video recording, you must create a new Recorder via createRecorder().

  9. Create a Depth Frame Output

    main

    The CameraDepthFrameOutput allows you to stream Depth frames in real-time, making them accessible via a JS worklet function. This requires react-native-vision-camera-worklets (and react-native-worklets) to be installed so that the onDepth callback can run synchronously on a parallel JS Worklet Runtime.

    You can implement this using three different API styles:

    1. Using the <Camera /> component: Pass the depthOutput to the outputs prop.
    2. Using the useCamera hook: Pass the depthOutput to the outputs array in the hook configuration.
    3. Using the imperative CameraSession API: Create the output via VisionCamera.createDepthFrameOutput, schedule the callback on a worklet runtime, and include it in the session configuration.
    // Example using the <Camera /> component
    function App() {
      const device = useCameraDevice('back')
      const depthOutput = useDepthOutput({
        onDepth(depth) {
          'worklet'
          console.log(`Received ${depth.width}x${depth.height} Depth!`)
          depth.dispose()
        }
      })
    
      return (
        <Camera
          style={StyleSheet.absoluteFill}
          isActive={true}
          device={device}
          outputs={[depthOutput]}
        />
      )
    }