Skip to content

App crashes - Thread 4: EXC_BAD_ACCESS (code=1, address=0x14e8258c0) #82

@lukachi

Description

@lukachi

Error: App crashes due to EXC_BAD_ACCESS error at ResizePlugin.mm

code to reproduce:

Just a simple skia frame processor to detect face and crop it.

const frameProcessor = useSkiaFrameProcessor(frame => {
    'worklet'
    frame.render()

    const _faces = detectFaces(frame)

    _faces.forEach((face, idx) => {
      const faceContainerPath = Skia.Path.Make()

      if (face.bounds) {
        const rect = Skia.XYWHRect(
          face.bounds.x,
          face.bounds.y,
          face.bounds.height,
          face.bounds.width,
        )
        faceContainerPath.addOval(rect)
        faceContainerPath.trim(0, scanProgressWorkletSharedValue.value, false)

        frame.save()

        if (!faceContainerPaints.value[idx]) {
          faceContainerPaints.value[idx] = Skia.Paint()
          faceContainerPaints.value[idx].setStyle(PaintStyle.Stroke)
          faceContainerPaints.value[idx].setStrokeWidth(4)
          faceContainerPaints.value[idx].setColor(Skia.Color('red'))
        }

        faceContainerPaints.value[idx].setColor(
          Skia.Color(face.yawAngle >= 8 || face.yawAngle <= -8 ? 'green' : 'red'),
        )

        frame.drawPath(faceContainerPath, faceContainerPaints.value[idx])

        frame.restore()

        runAtTargetFps(1, () => {
          'worklet'

          try {
            const resized = resize(frame, {
              scale: {
                width: 112,
                height: 112,
              },
              crop: {
                x: face.bounds.x,
                y: face.bounds.y,
                width: Math.max(face.bounds.width, face.bounds.height),
                height: Math.max(face.bounds.width, face.bounds.height),
              },
              pixelFormat: 'rgb',
              dataType: 'uint8',
            })

            onFaceResized(resized)
          } catch (error) {
            ErrorHandler.processWithoutFeedback(error)
          }
        })
      }
    })
  }, [])
Image

logs:

Image

I've tried to run it inside "runAsync" and without it, but the most stable variant was runAtTargetFps, so now this error happens not every time. Also starting metro server with empty cache helps. But anyway this error keep throwing after few second.

dependencies:

  • "react-native": "0.76.5",
  • "expo": "52.0.18",
  • "react-native-vision-camera": "4.6.3",
  • "react-native-vision-camera-face-detector": "^1.8.1",
  • "vision-camera-resize-plugin": "^3.2.0",
  • "react-native-worklets-core": "1.5.0",
  • "react-native-reanimated": "3.16.3",
  • "@shopify/react-native-skia": "^1.11.7",

tested on iphone 16 pro device

UPD:

current workaround i found from this post

// Cache array buffer to avoid it being constantly re-allocated
const CACHE_ID = '__cachedArrayForResizer'
function getArrayFromCache(size: number): Uint8Array {
  'worklet'
  if (global[CACHE_ID] == null || global[CACHE_ID].length != size) {
    global[CACHE_ID] = new Uint8Array(size)
  }
  return global[CACHE_ID]
}

type ResizeOptions = {
  width: number // target width, e.g., 256
  height: number // target height, e.g., 256
  cropX: number // starting x coordinate of the crop in the source frame
  cropY: number // starting y coordinate
  cropWidth: number // width of the crop region (e.g., face.bounds.width)
  cropHeight: number // height of the crop region
}

// Resize any Frame to the target width and height in RGB format.
export function resize(frame: Frame, opts: ResizeOptions): Uint8Array {
  'worklet'
  const inputWidth = frame.width
  const inputHeight = frame.height
  const arrayData = frame.toArrayBuffer()

  const outputSize = opts.width * opts.height * 3 // 3 for RGB
  const outputFrame = getArrayFromCache(outputSize)

  for (let y = 0; y < opts.height; y++) {
    for (let x = 0; x < opts.width; x++) {
      const srcX = Math.floor(opts.cropX + (x / opts.width) * opts.cropWidth)
      const srcY = Math.floor(opts.cropY + (y / opts.height) * opts.cropHeight)

      // Compute the source and destination index
      const srcIndex = (srcY * inputWidth + srcX) * 4 // 4 for BGRA
      const destIndex = (y * opts.width + x) * 3 // 3 for RGB

      // Convert from BGRA to RGB
      outputFrame[destIndex] = arrayData[srcIndex + 2] // R
      outputFrame[destIndex + 1] = arrayData[srcIndex + 1] // G
      outputFrame[destIndex + 2] = arrayData[srcIndex] // B
    }
  }

  return outputFrame
}

upd: turn out i've made resize plugins work more stable if i delay frame processing by couple seconds. But in a minute app crashes anyway.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions