-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Closed
Labels
🐛 bugSomething isn't workingSomething isn't working
Description
What's happening?
When recording a short video, facing this error, and the audio is not coming in the video.
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
- I am using Expo
- I have enabled Frame Processors (react-native-worklets-core)
- I have read the Troubleshooting Guide
- I agree to follow this project's Code of Conduct
- I searched for similar issues in this repository and found none.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
🐛 bugSomething isn't workingSomething isn't working
