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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions