Skip to content

🐛 Domain=AVFoundationErrorDomain Code=-11800 | The operation could not be completed | Error Domain=NSOSStatusErrorDo- main Code=-10868 #3524

@renishmithani

Description

@renishmithani

What's happening?

When recording a short video, facing this error, and the audio is not coming in the video.

Image

Reproduceable Code

// Parent component function for video recording
 const onLongPressIn = async () => {
    try {
      if (!cameraPermission || !microphonePermission) {
        return;
      }
      if (ref.current) {
        onToogleVideo();
        ref.current.recordVideo({
          fileType: 'mp4',
          flash: 'off',
          videoCodec: 'h265',
          onRecordingError: () => {
            setIsVideo(false);
          },
          onRecordingFinished: async video => {
            if (video.duration >= 1) {
              const thumbnail = await generaeThumbail({
                url: `file://${video.path}`,
                format: 'jpeg',
              });
              console.log(thumbnail.path, ' videoThumbnail');
              console.log(JSON.stringify(video), null, 2, ' video');
              dispatch({
                thumbnail: `${thumbnail.path}`,
                path: `file://${video.path}`,
                is_camera: false,
                type: getFileType(video.path).type,
                format: getFileType(video.path).format,
              });
              setIsVideo(false);
            } else {
              SnackBar({
                type: 'success',
                text1: 'video is too short for post',
              });
              setIsVideo(false);
            }
          },
        });
      }
    } catch (error) {}
  };

  const onPressOut = () => {
    if (isvideo && !isCameraError) {
      onFinishVideo();
    } else {
      setIsVideo(false);
      setIsCameraError(false);
      // if (isCameraError) {
      //   SnackBar({
      //     type: 'error',
      //     text1: 'Camera session have some issue',
      //   });
      // }
      onFinishVideo();
    }
  };

  const onFinishVideo = useCallback(async () => {
    if (ref.current) {
      await ref.current.stopRecording();
    }
  }, []);



// Child Component 
import {
  AppState,
  StyleSheet,
  Text,
  useWindowDimensions,
  View,
  Alert,
} from 'react-native';
import React, {
  useCallback,
  useEffect,
  useRef,
  useImperativeHandle,
  useState,
  memo,
} from 'react';
import {
  Camera as VisionCamera,
  PhotoFile,
  useCameraDevice,
  CameraPosition,
  RecordVideoOptions,
  TakePhotoOptions,
  CameraRuntimeError,
  useCameraPermission,
  CameraProps,
  useMicrophonePermission,
  FormatFilter,
  DeviceFilter,
  CameraDevice,
  useCameraFormat,
  getCameraFormat,
} from 'react-native-vision-camera';
import {
  requestCameraPermission,
  requestVideoAndAudioPermission,
} from 'utils/Permissions';
import {Heading} from './Text';
import {
  IS_IOS,
  UniStyleSheet,
  WINDOW_HEIGHT,
  WINDOW_WIDTH,
  hp,
} from 'styles/Dimensions';
import {useStyles} from 'react-native-unistyles';
import {ButtonCompact} from './Button';
import ImageResizer from '@bam.tech/react-native-image-resizer';
import {ASPECT_RATIO, ASPECT_RATIO_CAMERA_POST} from 'constants/AppConstant';
import SnackBar from './Snakbar';

export type TZoomLevel = 1 | 2 | 0.5;

interface Props extends Partial<CameraProps> {
  onClick?: () => void;
  children?: React.ReactNode;
  zoom?: number;
  onError: (error: CameraRuntimeError | any) => void;
  isActive?: boolean;
  deviceFormat?: FormatFilter[];
  getAvailableZoomLevel?: (v: number[], l: any) => void;
  dafaultCameraSide?: 'front' | 'back';
  useFormate: boolean;
  cameraPostStyle: boolean;
}

export type cameraRefType = {
  takePhoto: (
    options?: TakePhotoOptions,
  ) => Promise<Partial<TCameraResult> | undefined>;
  toogleTourch: (mode?: TTourchMode) => void;
  toogleCamera: () => void;
  recordVideo: (options: RecordVideoOptions) => void;
  stopRecording: () => Promise<void>;
  pauseRecord: () => Promise<void>;
  cancelRecording: () => Promise<void>;
  setZoomLevel: (zoomLevel: TZoomLevel) => void;
};

export type TFileType = 'image' | 'video';
export interface TCameraResult extends Pick<PhotoFile, 'path'> {
  type: TFileType;
  format?: 'mp4' | 'jpg' | 'png' | 'jpeg';
  thumbnail: string;
}
export const getFileType = (
  uri: string,
): Pick<TCameraResult, 'format' | 'type'> => {
  if (uri.includes('.jpeg')) {
    return {
      type: 'image',
      format: 'jpeg',
    };
  }
  if (uri.includes('.png')) {
    return {
      type: 'image',
      format: 'png',
    };
  }
  if (uri.includes('.jpg')) {
    return {
      type: 'image',
      format: 'jpg',
    };
  }
  return {
    type: 'video',
    format: 'mp4',
  };
};

const handleRotation = async (image: PhotoFile, quality: number = 60) => {
  try {
    let rotationAngle = 0;

    const rotatedImage = await ImageResizer.createResizedImage(
      `file://${image.path}`,
      image.width,
      image.height, 
      'JPEG',
      quality, 
      rotationAngle,
    );

    return rotatedImage;
  } catch (error) {
    console.error('Failed to rotate image:', error);
  }
};

const changeImageQuality = async (image: PhotoFile, quality: number = 10) => {
  const rotatedImage = await ImageResizer.createResizedImage(
    `file://${image.path}`,
    image.width,
    image.height, 
    'JPEG',
    quality, 
    undefined, 
  );
  return rotatedImage;
};

export type TTourchMode = 'on' | 'off' | 'auto';

const Camera = React.forwardRef<cameraRefType, Props>(
  (
    {
      onError,
      style,
      deviceFormat = [],
      dafaultCameraSide = 'front',
      useFormate = false,
      cameraPostStyle = false,
      getAvailableZoomLevel = () => null,
      ...props
    },
    ref,
  ) => {
    const {styles} = useStyles(CameraStyle);
    const {hasPermission: camerPermission} = useCameraPermission();
    const {hasPermission: microphonePermission} = useMicrophonePermission();
    const CameraRef = useRef<VisionCamera>(null);
    const [cameraFormat, setCameraFormat] =
      useState<FormatFilter[]>(deviceFormat);
    const [device, setDevice] = useState<CameraPosition>(dafaultCameraSide);
    const [torchMode, setTorchMode] = useState<TTourchMode>('off');

    const [appState, setAppState] = useState(AppState.currentState);
    const [isCameraActive, setIsCameraActive] = useState(true);

    const deviceFilter: DeviceFilter = {
      physicalDevices: [
        'ultra-wide-angle-camera',
        'wide-angle-camera',
        'telephoto-camera',
      ],
    };
    const cameraDevice: CameraDevice | undefined = useCameraDevice(
      device,
      deviceFilter,
    );

    const availableZoomLevels = [
      cameraDevice?.minZoom || 1,
      cameraDevice?.neutralZoom || 1,
      (cameraDevice?.neutralZoom || 1) * 2, 
    ].filter(
      zoom =>
        zoom >= (cameraDevice?.minZoom || 1) &&
        zoom <= (cameraDevice?.maxZoom || 2),
    );

    const [deviceZoom, setDeviceZoom] = useState(cameraDevice?.neutralZoom);

    const requestCaptureVideoAndAudio = useCallback(async () => {
      try {
        if (props?.audio) {
          await requestVideoAndAudioPermission();
        } else {
          await requestCameraPermission();
        }
      } catch (error) {
        console.log(error);
      }
    }, [props.audio]);

    useEffect(() => {
      requestCaptureVideoAndAudio();
    }, [requestCaptureVideoAndAudio]);

    useImperativeHandle(
      ref,
      () => {
        return {
          changeCameraFormat: (changeformat: FormatFilter[]) => {
            if (CameraRef.current) {
              // setCameraFormat(changeformat);
            }
          },
          takePhoto: async (
            options?: TakePhotoOptions,
          ): Promise<Partial<TCameraResult> | undefined> => {
            try {
              if (CameraRef.current) {
                const result = await CameraRef.current.takePhoto(options);
                const resizedImage = await handleRotation(result);
                const thumbnailImage = await changeImageQuality(result, 10);
                const type = getFileType(result.path);

                return {
                  ...result,
                  path: `file://${resizedImage?.path}`,
                  type: type.type,
                  format: type.format,
                  thumbnail: `file://${thumbnailImage.path}`,
                };
              }
            } catch (error) {
              onError(error);
            }
          },
          recordVideo: async (options: RecordVideoOptions) => {
            try {
              if (CameraRef.current) {
                CameraRef.current.startRecording(options);
              }
            } catch (error) {
              onError(error);
            }
          },
          cancelRecording: async () => {
            try {
              if (CameraRef.current) {
                await CameraRef.current.cancelRecording();
              }
            } catch (error) {
              onError(error);
            }
          },
          stopRecording: async () => {
            try {
              if (CameraRef.current) {
                await CameraRef.current.stopRecording();
              }
            } catch (error) {
              onError(error);
            }
          },
          pauseRecord: async () => {
            try {
              if (CameraRef.current) {
                await CameraRef.current.pauseRecording();
              }
            } catch (error) {
              onError(error);
            }
          },
          toogleTourch: () => {
            try {
              if (torchMode === 'on') {
                setTorchMode('off');
              } else {
                setTorchMode('on');
              }
            } catch (error) {
              onError(error);
            }
          },

          toogleCamera: () => {
            try {
              if (device === 'back') {
                setDeviceZoom(1);
                setDevice('front');
              } else {
                setDeviceZoom(cameraDevice?.neutralZoom);
                setDevice('back');
              }
            } catch (error) {
              onError(error);
            }
          },
          setZoomLevel: (zoomLevel: number) => {
            if (device === 'front') {
              setDeviceZoom(zoomLevel);
            } else if (device === 'back') {
              if (zoomLevel === 0.5) {
                setDeviceZoom(0.5);
              } else if (zoomLevel === 2) {
                setDeviceZoom(2);
              } else {
                setDeviceZoom(zoomLevel);
              }
            }
          },
        };
      },
      [onError, torchMode, cameraDevice?.neutralZoom, device],
    );

    useEffect(() => {
      if (availableZoomLevels) {
        const zoomL = [...new Set(availableZoomLevels || [])].reverse();
        const lenses = {
          minZoom: cameraDevice?.minZoom,
          maxZoom: cameraDevice?.maxZoom,
          neutralZoom: cameraDevice?.neutralZoom,
        };
        getAvailableZoomLevel(zoomL, lenses);
      }
    }, []);

    useEffect(() => {
      const subscription = AppState.addEventListener('change', nextAppState => {
        setAppState(nextAppState);
        if (nextAppState === 'active') {
          setIsCameraActive(true);
        } else {
          setIsCameraActive(false);
        }
      });

      return () => {
        subscription.remove();
      };
    }, []);

    if (!cameraDevice) {
      return (
        <View style={styles.container}>
          <Heading color="white" size="large" weight="bold">
            No Device Found
          </Heading>
        </View>
      );
    }

    if (!camerPermission || (props?.audio && !microphonePermission)) {
      return (
        <View style={[styles.container]}>
          <Heading color="white" size="large" weight="bold">
            {props.audio
              ? 'Camera and Microphone Permission Required!'
              : 'Camera Permission Required!'}
          </Heading>
          <ButtonCompact
            buttonStyle={{
              position: 'relative',
              zIndex: 1000,
            }}
            variants="white"
            onPress={requestCaptureVideoAndAudio}
            label="Allow now"
          />
        </View>
      );
    }
    return <VisionCamera
        photo
        onError={onError}
        enableZoomGesture={false}
        zoom={deviceZoom}
        ref={CameraRef}
        style={[style, StyleSheet.absoluteFill]}
        device={cameraDevice}
        isActive={isCameraActive}
        outputOrientation="preview"
        {...props}
      />
  },
);

const CameraStyle = UniStyleSheet({
  container: {
    gap: hp(1),
    flex: 1,
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    height: WINDOW_HEIGHT,
    width: WINDOW_WIDTH,
    justifyContent: 'center',
    alignItems: 'center',
  },
});
export default memo(Camera);

Relevant log output

{"name": "unknown/
unknown","_code":"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=0x300b74060
{Error Domain=NSOSStatusErrorDo-
main Code=-10868 \"(null)
("""_cause": "code": -11800,"details":
{"NSLocalizedFailureReason": "An unknown error occurred
(-10868)","NSUnderlyingError":null,"N
SLocalizedDescription":"The operation could not be
completed"), "domain":"AVFoundation-ErrorDomain", "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=0x300b74060
{Error Domain=NSOSStatusErrorDo-main Code=-10868 \"(null) \"}"}}

Camera Device

{
  "maxZoom": 16,
  "id": "com.apple.avfoundation.avcapturedevice.built-in_video:3",
  "position": "back",
  "formats": [],
  "minFocusDistance": 12,
  "name": "Back Dual Camera",
  "hasFlash": true,
  "minExposure": -8,
  "isMultiCam": true,
  "maxExposure": 8,
  "supportsLowLightBoost": false,
  "sensorOrientation": "portrait",
  "supportsRawCapture": false,
  "neutralZoom": 1,
  "physicalDevices": [
    "wide-angle-camera",
    "telephoto-camera"
  ],
  "supportsFocus": true,
  "minZoom": 1,
  "hasTorch": true,
  "hardwareLevel": "full"
}

Device

iPhone 16 pro max (iOS 18), iPhone Xs (iOS 18)

VisionCamera Version

4.5.1

Can you reproduce this issue in the VisionCamera Example app?

Yes, I can reproduce the same issue in the Example app here

Additional information

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions