mrousavy / react-native-vision-camera

📸 A powerful, high-performance React Native Camera library.
https://react-native-vision-camera.com
MIT License
7.54k stars 1.1k forks source link

🐛 CameraRuntimeError: Unknown / Unknown: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" #2632

Closed ChristopherGabba closed 6 months ago

ChristopherGabba commented 8 months ago

What's happening?

I'm using a custom countdown timer component to invoke a callback when the timer hits 0 to start the camera recording. When the timer hits 0, sometimes the camera records perfectly, other times it throws the error above. I've narrowed the cause down to my countdown component. When I start the recording with a button, it works perfectly every time.

Reproduceable Code

Take this page and copy it directly. Upon countdown completion, you will get this to replicate. If it doesn't work the first time, try it several times until it works.

```tsx CAMERA TEST PAGE: import { observer } from "mobx-react-lite" import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react" import { Dimensions, View, ViewStyle } from "react-native" import { AppStackScreenProps } from "app/navigators" import { colors } from "../../theme" import { Camera, CameraRuntimeError, useCameraDevice, useCameraFormat, } from "react-native-vision-camera" import { useIsForeground } from "app/hooks/useIsForeground" import { useIsFocused } from "@react-navigation/native" import Video, { IgnoreSilentSwitchType, ResizeMode } from "react-native-video" import { Button } from "app/components" import { PreVideoThumbnailBlur } from "./PreVideoThumbnailBlur" import { CountdownTimer } from "./CountdownTimer" import { TimerStatus } from "./timer.types" const { width, height } = Dimensions.get("window") interface CameraDebugProps extends AppStackScreenProps<"CameraDebug"> {} export const CameraDebugScreen: FC = observer(function RecordingScreen( _props, // @demo remove-current-line ) { const { navigation } = _props // #region - initializations const cameraRef = useRef(null) const [initialCountTimerStatus, setInitialCountdownTimerStatus] = useState("unstarted") const [cameraReady, setCameraReady] = useState(false) const [videoReady, setVideoReady] = useState(false) const [isVertical, setIsVertical] = useState(false) const isFocused = useIsFocused() const isForeground = useIsForeground() const isActive = useMemo(() => { return isFocused && isForeground }, [isFocused, isForeground]) const device = useCameraDevice("front") const [targetFps] = useState(30) const cameraAspectRatio = 130 / 80 const format = useCameraFormat(device, [ { videoStabilizationMode: "standard" }, { fps: targetFps }, { videoAspectRatio: cameraAspectRatio }, { videoResolution: { width: 640, height: 480 } }, { photoAspectRatio: cameraAspectRatio }, { photoResolution: { width: 640, height: 480 } }, ]) // #endregion useEffect(() => { if (cameraReady && videoReady) { setInitialCountdownTimerStatus('play') } }, [cameraReady, videoReady]) /** * This is just a random 10 second timer to stop everything */ useEffect(() => { setTimeout(() => stopEverything(), 10000) }, []) const [shouldPlay, setShouldPlay] = useState(false) const [recordedVideoUrl, setRecordedVideoUrl] = useState("") const [recordedDuration, setRecordedDuration] = useState() /** * Begin the recording sequence and media playback */ async function startEverything() { setShouldPlay(true) cameraRef.current?.startRecording({ fileType: "mp4", onRecordingFinished: (video) => { console.log("RECORDING FINISHED", video) setRecordedVideoUrl(video.path) setRecordedDuration(video.duration) }, onRecordingError: (error) => { console.error("Camera Runtime Error", error) }, }) } /** * Callback to tell the camera to stop recording and * the video to stop playing */ async function stopEverything() { if (!!recordedVideoUrl) return setShouldPlay(false) cameraRef.current?.stopRecording() } const onCameraError = useCallback( (error: CameraRuntimeError) => { switch (error.code) { case "session/audio-in-use-by-other-app": { alert("The camera or microphone is in use by another app") setShouldPlay(false) navigation.goBack() } default: break } // setCameraError(error) console.error(error) }, [setShouldPlay], ) return (

Relevant log output

Error occurred: The operation couldn’t be completed. (OSStatus error -50.)

VisionCamera.startRecording(options:onVideoRecorded:onError:): Starting Video recording...
VisionCamera.startRecording(options:onVideoRecorded:onError:): Will record to temporary file: /private/var/mobile/Containers/Data/Application/F6A8B890-54F1-4ACB-923D-A0D52E4CEFCF/tmp/ReactNative/7D9FC98E-225A-4DD0-B636-354A7EE6922B.mp4
VisionCamera.startRecording(options:onVideoRecorded:onError:): Enabling Audio for Recording...
VisionCamera.activateAudioSession(): Activating Audio Session...
VisionCamera.updateCategory(_:mode:options:): Changing AVAudioSession category from AVAudioSessionCategoryPlayback -> AVAudioSessionCategoryPlayAndRecord
VisionCamera.initializeAudioWriter(withSettings:format:): Initializing Audio AssetWriter with settings: ["AVEncoderQualityForVBRKey": 91, "AVEncoderBitRatePerChannelKey": 96000, "AVFormatIDKey": 1633772320, "AVNumberOfChannelsKey": 1, "AVSampleRateKey": 44100, "AVEncoderBitRateStrategyKey": AVAudioBitRateStrategy_Variable]
VisionCamera.initializeAudioWriter(withSettings:format:): Initialized Audio AssetWriter.
VisionCamera.initializeVideoWriter(withSettings:): Initializing Video AssetWriter with settings: ["AVVideoCodecKey": hvc1, "AVVideoHeightKey": 960, "AVVideoWidthKey": 540, "AVVideoCompressionPropertiesKey": {
    AllowFrameReordering = 1;
    AllowOpenGOP = 1;
    AverageBitRate = 4727808;
    ExpectedFrameRate = 30;
    MaxAllowedFrameQP = 41;
    MaxKeyFrameIntervalDuration = 1;
    MinAllowedFrameQP = 15;
    MinimizeMemoryUsage = 1;
    Priority = 80;
    ProfileLevel = "HEVC_Main_AutoLevel";
    RealTime = 1;
    RelaxAverageBitRateTarget = 1;
}]
VisionCamera.initializeVideoWriter(withSettings:): Initialized Video AssetWriter.
VisionCamera.start(clock:): Starting Asset Writer(s)...
VisionCamera.updateCategory(_:mode:options:): AVAudioSession category changed!
<<<< FigSharedMemPool >>>> Fig assert: "blkHdr->useCount > 0" at  (FigSharedMemPool.c:591) - (err=0)
VisionCamera.sessionRuntimeError(notification:): Unexpected Camera Runtime Error occured!
VisionCamera.start(clock:): Asset Writer(s) started!
VisionCamera.start(clock:): Started RecordingSession at time: 408672.754171666
VisionCamera.startRecording(options:onVideoRecorded:onError:): RecordingSesssion started in 803.766375ms!
VisionCamera.onError(_:): Invoking onError(): Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-10868), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x28399aa00 {Error Domain=NSOSStatusErrorDomain Code=-10868 "(null)"}}
VisionCamera.activateAudioSession(): Audio Session activated!
CameraRuntimeError: unknown/unknown
Message: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-10868), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x28399aa00 {Error Domain=NSOSStatusErrorDomain Code=-10868 "(null)"}}
Code: unknown/unknown
Func: onCameraError
Screen: Recording
Comp: ReelFeelPlayer
'reactionUrl?', false
taking thumbnail in stop everything
[I] <libMMKV.mm:301::-[MMKV onMemoryWarning]> cleaning on memory warning mmkv.default
[I] <MMKV.cpp:308::clearMemoryCache> clearMemoryCache [mmkv.default]
[I] <MemoryFile.cpp:103::close> closing fd[0x15], /var/mobile/Containers/Data/Application/F6A8B890-54F1-4ACB-923D-A0D52E4CEFCF/Documents/mmkv/mmkv.default
VisionCamera.takePhoto(options:promise:): Capturing photo...
VisionCamera.stop(clock:): Requesting stop at 408673.167640708 seconds for AssetWriter with status "writing"...
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0305 14:57:14.502342 1846079488 JSIExecutor.cpp:376] Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC

VisionCamera.stop(clock:): Waited 4.0 seconds but no late Frames came in, aborting capture...
VisionCamera.finish(): Stopping AssetWriter with status "writing"...
VisionCamera.startRecording(options:onVideoRecorded:onError:): RecordingSession finished with status completed.
VisionCamera.deactivateAudioSession(): Deactivating Audio Session...
VisionCamera.deactivateAudioSession(): Audio Session deactivated!
'RECORDING FINISHED', { path: 'file:///private/var/mobile/Containers/Data/Application/F6A8B890-54F1-4ACB-923D-A0D52E4CEFCF/tmp/ReactNative/7D9FC98E-225A-4DD0-B636-354A7EE6922B.mp4',
  width: 0,
  height: 0,
  duration: 0.442151875 }

Camera Device

{
  "minFocusDistance": 0,
  "formats": [],
  "minZoom": 1,
  "sensorOrientation": "landscape-right",
  "isMultiCam": false,
  "maxZoom": 128.875,
  "name": "Front Camera",
  "hasFlash": true,
  "minExposure": -8,
  "id": "com.apple.avfoundation.avcapturedevice.built-in_video:1",
  "supportsLowLightBoost": false,
  "maxExposure": 8,
  "neutralZoom": 1,
  "physicalDevices": [
    "wide-angle-camera"
  ],
  "supportsFocus": false,
  "supportsRawCapture": false,
  "hasTorch": false,
  "position": "front",
  "hardwareLevel": "full"
}

Device

iPhone 12 Physical Device

VisionCamera Version

3.9.0

Can you reproduce this issue in the VisionCamera Example app?

No, I cannot reproduce the issue in the Example app

Additional information

mrousavy commented 8 months ago

What exactly is Error -10868 again? I remember seeing this somewhere

ChristopherGabba commented 8 months ago

@mrousavy I did a bunch of googling and docs reading on it, but truthfully everyone says that "it doesn't provide any helpful info".

Hate to be that guy, but I asked GPT-4 as a last resort and the response was:

GPT Response The AVFoundationErrorDomain Code=-11800 is an error that occurs in the context of iOS development when dealing with media playback, and it generally indicates that "The operation could not be completed". It's a broad error that can be caused by various issues related to media loading and playback, often indicating that the media file could not be loaded due to issues with its format, encoding, or the way it is being accessed. Several common reasons for this error could be: Insecure HTTP URLs being blocked by App Transport Security in iOS, requiring HTTPS instead, or needing exceptions configured in Info.plist if HTTP must be used. Incorrect or missing file extensions for the media resources, which can lead to the system being unable to recognize the file format. Issues with the media file encoding or how it's being served from the backend (such as incorrect Content-Type headers or streaming configurations). Developers have found that verifying the URL, ensuring that the file has the correct extension, and confirming that the backend is properly configured to serve media files can help to resolve this issue. If the issue occurs only in a simulator and not on a real device, it might also be linked to the simulator's limitations or bugs.

I know that it isn't coming from the react-native-video package because it's being thrown in the onCameraError package. The resolution to this issue may be just "hey fix your shit timer code" lol. But figured it may be some edge case you could protect for in the react-native-vision-camera package. I spent a good amount of time making a reproducible demo for you and adding logs this time, hopefully it doesn't take you long to dig into.

There is a line in the logs implying that some sort of state operation interrupted the camera:

VisionCamera.updateCategory(_:mode:options:): AVAudioSession category changed!
<<<< FigSharedMemPool >>>> Fig assert: "blkHdr->useCount > 0" at  (FigSharedMemPool.c:591) - (err=0)
VisionCamera.sessionRuntimeError(notification:): Unexpected Camera Runtime Error occured!
ChristopherGabba commented 8 months ago

@mrousavy Okay so this issue has plagued me now for a few weeks and I've tried a lot of updates.

  1. I rewrote the timer logic and it's still occurring.
  2. I just changed the react-native-video package to expo-av's video package and the error is gone.

I'm going to assume it's some sort of conflict between react-native-vision-camera and react-native-video. Maybe the AVAudioSessions or something?

mrousavy commented 8 months ago

Good point, yea it could be the audio sessions.

mrousavy commented 6 months ago

Closing this for now, as I think I am doing nothing wrong in my audio session - maybe RN Video doesn#t clean up after they are done with audio.