Can't record video [Error: Argument appears to not be a ReactComponent.]
colbymchenry opened this issue · 2 comments
Requirements:
Please go through this checklist before opening a new issue
- Review the documentation
- Search for existing issues in: viromedia/viro & ViroCommunity/viro
- Use the latest ViroReact release
Environment
Developing on Mac OS,
"@viro-community/react-viro": "^2.23.0", "react-native": "0.71.7",
Device is iPhone 14 Pro Max
Description
For whatever reason I'm getting this error when I attempt to start recording:
recording error: [Error: Argument appears to not be a ReactComponent. Keys: push,pop,popN,jump,replace,startVideoRecording,stopVideoRecording,takeScreenshot,resetARSession,setWorldOrigin,project,unproject,viroAppProps]
Reproducible Demo
Here is my code to help visualize what I have:
You'll notice I use a Wrapper class that extends React.Component. This was something I found here:
#137 but didn't seem to help
import React, {useEffect, useMemo, useState} from 'react';
import {StyleSheet, View} from 'react-native';
import {type NativeStackScreenProps} from '@react-navigation/native-stack';
import {RootStackParamList} from '../../../../../routes';
import {
ViroARScene,
Viro3DObject,
ViroARSceneNavigator,
ViroDirectionalLight,
ViroARPlaneSelector,
ViroNode,
ViroButton,
ViroPolyline,
ViroMaterials,
} from '@viro-community/react-viro';
import {Button, Text} from 'react-native-paper';
import Svg, {Path} from 'react-native-svg';
import {Viro3DPoint} from '@viro-community/react-viro/dist/components/Types/ViroUtils';
import {Slider} from '@miblanchard/react-native-slider';
import ARProvider, {useAR} from './context';
const recordButtonSize = 80;
const styles = StyleSheet.create({
planeSelector: {
backgroundColor: 'red',
},
marginAuto: {
margin: '0 auto',
},
recordButton: {
minHeight: recordButtonSize,
minWidth: recordButtonSize,
maxHeight: recordButtonSize,
maxWidth: recordButtonSize,
borderRadius: 100,
backgroundColor: 'rgba(255, 255, 255, 0.6)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
},
recordButtonContent: {
marginLeft: -4,
marginBottom: -5,
},
sliderContainer: {
width: '100%',
},
sliderThumb: {
backgroundColor: 'white',
},
sliderTrack: {
backgroundColor: 'white',
},
});
const MODEL_HEIGHT = 0.83;
const TOP_WIDTH = 0.29;
const BUTTON_SIZE = 0.2;
const SIDES = [
{
side: 'back',
source: require('../../../../assets/amni3dmodel_back.glb'),
btnPosition: [0, 0.5, -0.5],
btnRotation: [-160, 0, 0],
polylines: [
[-0.57, 0.1, -0.65],
[0.57, 0.1, -0.65],
[0.29, MODEL_HEIGHT - 0.05, -0.36],
[-0.29, MODEL_HEIGHT - 0.05, -0.36],
[-0.57, 0.1, -0.65],
],
},
{
side: 'front',
source: require('../../../../assets/amni3dmodel_front.glb'),
btnPosition: [0, 0.5, 0.5],
btnRotation: [-25, 0, 0],
polylines: [
[0.57, 0.1, 0.65],
[-0.57, 0.1, 0.65],
[-0.29, MODEL_HEIGHT - 0.05, 0.36],
[0.29, MODEL_HEIGHT - 0.05, 0.36],
[0.57, 0.1, 0.65],
],
},
{
side: 'left',
source: require('../../../../assets/amni3dmodel_left.glb'),
btnPosition: [-0.5, 0.5, 0],
btnRotation: [-80, 90, 75],
polylines: [
[-0.65, 0.1, 0.57],
[-0.65, 0.1, -0.57],
[-0.36, 0.78, -0.29],
[-0.36, 0.78, 0.29],
[-0.65, 0.1, 0.57],
],
},
{
side: 'right',
source: require('../../../../assets/amni3dmodel_right.glb'),
btnPosition: [0.5, 0.5, 0],
btnRotation: [90, 10, 115],
polylines: [
[0.65, 0.1, 0.57],
[0.65, 0.1, -0.57],
[0.36, MODEL_HEIGHT - 0.05, -0.29],
[0.36, MODEL_HEIGHT - 0.05, 0.29],
[0.65, 0.1, 0.57],
],
},
{
side: 'top',
source: require('../../../../assets/amni3dmodel_top.glb'),
btnPosition: [0, MODEL_HEIGHT + 0.02, 0],
btnRotation: [25, 90, 115],
polylines: [
[TOP_WIDTH, MODEL_HEIGHT, TOP_WIDTH],
[TOP_WIDTH, MODEL_HEIGHT, -TOP_WIDTH],
[-TOP_WIDTH, MODEL_HEIGHT, -TOP_WIDTH],
[-TOP_WIDTH, MODEL_HEIGHT, TOP_WIDTH],
[TOP_WIDTH, MODEL_HEIGHT, TOP_WIDTH],
],
},
];
class Wrapper extends React.Component {
render() {
return <HelloWorldSceneAR setReset={() => {}} />;
}
}
const HelloWorldSceneAR = (props: any) => {
const arRef: any = React.useRef();
const arSceneRef: any = React.useRef();
const [plane, setPlane] = useState<any>();
const [position, setPosition] = useState<Viro3DPoint>([0, 0, 0]);
const [accumulatedScale, setAccumulatedScale] = useState<number>(1); // start at a scale of 1
const [activePinchScale, setActivePinchScale] = useState<number>(1); // active pinch scale starts at 1
const {arValues, setArValues} = useAR();
const [animatedOpacity, setAnimatedOpacity] = useState<number>(0); // The animated opacity value for the hovering effect
const [activeSide, setActiveSide] = useState<string>(''); // What side the user is hovering on
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function onInitialized(state: any, reason: any) {}
const reset = () => {
if (arRef.current) {
arRef.current.reset();
}
setPosition([0, 0, 0]);
setPlane(undefined);
setAccumulatedScale(1);
setActivePinchScale(1);
};
const modelScale = useMemo(() => {
return (
(plane ? plane.width * 0.35 : 0.25) * accumulatedScale * activePinchScale
);
}, [plane, activePinchScale, accumulatedScale]);
const handlePinchEffect = (pinchState: number, scaleFactor: number) => {
if (pinchState === 3) {
// end of pinch gesture
// Update the accumulated scale with the final scale for this pinch gesture
setAccumulatedScale(
oldAccumulatedScale => oldAccumulatedScale * activePinchScale,
);
// Reset active pinch scale
setActivePinchScale(1);
} else {
// ongoing pinch gesture
setActivePinchScale(scaleFactor);
}
};
useEffect(() => {
props.setReset(() => {
return () => reset();
});
}, []);
const onButtonHover = (side: string, isHovering: boolean) => {
setActiveSide(isHovering ? side : '');
};
// Function to start the opacity animation
const startOpacityAnimation = () => {
let opacity = 0;
setAnimatedOpacity(0);
const animationInterval = setInterval(() => {
opacity += 0.2; // You can adjust the step size as needed
if (opacity >= 1) {
clearInterval(animationInterval);
} else {
setAnimatedOpacity(opacity);
}
}, 100); // You can adjust the interval duration as needed
};
// Start the animation every time the active side changes
useEffect(() => {
startOpacityAnimation();
}, [activeSide]);
ViroMaterials.createMaterials({
white: {
lightingModel: 'Blinn',
diffuseTexture: require('../../../../assets/white.jpg'),
specularTexture: require('../../../../assets/white.jpg'),
},
});
const renderSide = (arg: any) => {
return (
<>
<Viro3DObject
source={arg.source}
type='GLB'
highAccuracyEvents={true}
/>
<ViroButton
source={require('../../../../assets/blue_reticle.png')}
gazeSource={require('../../../../assets/blue_reticle.png')}
tapSource={require('../../../../assets/blue_reticle.png')}
position={arg.btnPosition}
rotation={arg.btnRotation}
height={BUTTON_SIZE}
width={BUTTON_SIZE}
renderingOrder={1}
onHover={(isHovering: boolean) => onButtonHover(arg.side, isHovering)}
// onTap={this._onButtonTap}
/>
<ViroPolyline
position={[0, 0, 0]}
points={arg.polylines}
thickness={0.005}
materials={'white'}
ignoreEventHandling={true}
opacity={activeSide === arg.side ? animatedOpacity : 0}
/>
</>
);
};
return (
<ViroARScene
onTrackingUpdated={onInitialized}
// onAnchorFound={anchor => console.log()}
// onAnchorUpdated={() => console.log('onAnchorUpdated')}
// onAnchorRemoved={() => console.log('onAnchorRemoved')}
ref={arSceneRef}
>
<ViroARPlaneSelector
minHeight={0.1}
minWidth={0.1}
onPlaneSelected={e => setPlane(e)}
alignment='Horizontal'
style={styles.planeSelector}
ref={arRef}
>
{/* Two supportive lights, casting inside the model and outside */}
<ViroDirectionalLight
color='#FFFFFF'
intensity={4500}
direction={[-1, -1, -1]}
shadowOrthographicPosition={[0, 3, -5]}
shadowOrthographicSize={10}
shadowNearZ={2}
shadowFarZ={9}
castsShadow={true}
shadowOpacity={1}
/>
<ViroDirectionalLight
color='#FFFFFF'
intensity={4500}
direction={[0, -1, 0]}
shadowOrthographicPosition={[0, 3, -5]}
shadowOrthographicSize={10}
shadowNearZ={2}
shadowFarZ={9}
castsShadow={true}
shadowOpacity={1}
/>
{/* Node/container for the entire modal, buttons, and polylines */}
<ViroNode
position={position}
scale={[modelScale, modelScale, modelScale]}
onPinch={handlePinchEffect}
onDrag={() => {}}
dragType='FixedToWorld'
rotation={[0, arValues.rotation || 0, 0]}
>
{SIDES.map((obj: any) => {
return renderSide(obj);
})}
</ViroNode>
</ViroARPlaneSelector>
</ViroARScene>
);
};
const MainScreen: React.FC<NativeStackScreenProps<RootStackParamList>> = () => {
const [recording, setRecording] = useState<boolean>(false);
const [reset, setReset] = useState<any>({});
const {arValues, setArValues} = useAR();
const [viroRef, setViroRef] = useState<ViroARSceneNavigator>();
const onRecordBtnPress = async () => {
// if (recording) {
// await stopVideoRecording();
// } else {
startVideoRecording();
// }
};
const startVideoRecording = () => {
function onErr(error: any) {
console.error(error);
}
if (
!viroRef ||
!viroRef.sceneNavigator ||
!viroRef.sceneNavigator.startVideoRecording
) {
console.error('viroRef has not been loaded');
} else {
let now: any = new Date();
now = now.toISOString();
now = now.replace(/\:|\./g, '-');
let fileName = '_' + now;
try {
viroRef.sceneNavigator.startVideoRecording(fileName, false, onErr);
} catch (err) {
console.log('recording error:', err);
}
}
};
const stopVideoRecording = async () => {
// if (!recording) {
// return;
// }
// const resp: {
// success: boolean;
// url: string;
// errorCode: number;
// } = await arSceneNavigatorRef.arSceneNavigator.stopVideoRecording();
// if (!resp.success) {
// // warn user of failure
// return;
// }
// setRecording(false);
// // let user know of success
};
return (
<View className='relative flex flex-column flex-1'>
<ViroARSceneNavigator
ref={(c: ViroARSceneNavigator) => setViroRef(c)}
autofocus={true}
pbrEnabled={true}
shadowsEnabled={true}
initialScene={{
scene: Wrapper,
}}
/>
{/* Start Reset Plane Detection Code */}
<View className='absolute top-2 right-0 z-10'>
<Button
className='bg-transparent p-2'
onPress={() => {
reset();
}}
mode='elevated'
>
<Svg
fill='none'
viewBox='0 0 24 24'
strokeWidth='1.5'
stroke='#FFF'
className='w-12 h-12'
>
</Svg>
</Button>
</View>
{/* End Reset Plane Detection Code */}
{/* Start Record Button Code */}
<View className='absolute left-0 bottom-12 z-10 w-full flex items-center justify-center'>
<Button
onPress={onRecordBtnPress}
mode='elevated'
style={styles.recordButton}
contentStyle={styles.recordButtonContent}
>
{recording ? (
<View className='bg-red-500 rounded-sm w-10 h-10' />
) : (
<View className='bg-gray-400/30 rounded-full w-10 h-10' />
)}
</Button>
</View>
{/* End Record Button Code */}
{/* Start Rotation Slider Code */}
<View className='absolute left-0 bottom-0 z-10 w-full flex items-center justify-center'>
<Slider
value={arValues.rotation}
onValueChange={value =>
setArValues((oldValues: any) => {
return {...oldValues, rotation: value[0]};
})
}
maximumValue={359}
minimumValue={0}
step={1}
containerStyle={styles.sliderContainer}
thumbStyle={styles.sliderThumb}
trackStyle={styles.sliderTrack}
disabled={recording}
/>
<Text className='absolute right-0 bottom-8'>{arValues.rotation}°</Text>
</View>
{/* End Rotation Slider Code */}
{/* Start Reticle Code */}
<View
className='absolute z-10 top-0 left-0 w-full h-full flex justify-center items-center'
pointerEvents='none'
>
<View className='flex text-red-500'>
<Svg viewBox='0 0 24 24' fill='#FFF' className='absolute w-12 h-12'>
</Svg>
<Svg
viewBox='0 0 24 24'
fill='none'
stroke='#FFF'
strokeWidth='1'
strokeLinecap='round'
strokeLinejoin='round'
className='scale-[2] w-12 h-12 lucide lucide-scan'
>
</Svg>
</View>
</View>
{/* End Reticle Code */}
</View>
);
};
export const AutoLabelScreen: React.FC<
NativeStackScreenProps<RootStackParamList>
> = props => {
return (
<ARProvider>
<MainScreen {...props} />
</ARProvider>
);
};
Same pb, any solution here?
I've fixed this in 2.23.2-beta
and I'm hoping to verify on Android before I ship a release. Can you verify that this version fixed your issue?