I've just skimmed through documents and got a question.
Can I record or capture photo/video with AR and save them?
Yes, you can record and capture photos/video with AR and save them. See the async takeScreenshot and startVideoRecording methods here -> http://docs.viromedia.com/docs/viroarscenenavigator#methods
FYI to anyone that comes across this issue while trying to get screenshots/video recording to work, you cannot call startVideoRecording, stopVideoRecording, or takeScreenshot directly from a ref to a ViroARSceneNavigator component, you must use this.refName.sceneNavigator.takeScreenshot instead.
Examle ViroARSceneNavigator component with Ref:
<ViroARSceneNavigator
ref={(c) => this._arScene = c}
... other props ...
/>
This will NOT work:
this._arScene.takeScreenshot(fileName: string, saveToCameraRoll: bool) // <-- JS Error: takeScreenshot is undefined
This WILL work:
this._arScene.sceneNavigator.takeScreenshot(fileName: string, saveToCameraRoll: bool) // <-- Yeww! It works
this._arScene.sceneNavigator.startVideoRecording(fileName: string, saveToCameraRoll: bool, onError: func) // <-- Yeww! It works
this._arScene.sceneNavigator.stopVideoRecording() // <-- Yeww! It works
.. and so will this (these how the functions are named in node_modules/react-
viro/components/AR/ViroARSceneNavigator.js):
this._arScene._takeScreenshot(fileName: string, saveToCameraRoll: bool)
this._arScene._startVideoRecording(fileName: string, saveToCameraRoll: bool, onError: func)
this._arScene._stopVideoRecording()
@dam00n Point 1 in the documentation at https://docs.viromedia.com/docs/viroarscenenavigator#methods suggests that these methods are accessible directly from the ref:
- Use the built-in React Native ref property to grab a reference
Which is incorrect for these three functions.
Perhaps the docs should be updated as per the findings above, or the underlying implementation fixed to be able to access these functions directly from a ref using the intended function names
@se1exin , Thanks for catching and reporting this! I have updated our docs to reflect this. Devs can access these methods (and others) via the SceneNavigator as this.props.sceneNavigator.takeScreenshot.
i am using this.props.sceneNavigator.startVideoRecording, this.props.sceneNavigator.stopVideoRecording & takeScreenshot but it's not saving the video or picture on my phone
Hi @Noor0, are you developing with our Testbed App or with Android Studio? if you are developing and running your app with Android Studio make sure your manifest file has the proper permissions such as:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Further, check the onError callback from the startVideoRecording() method to see what error you are getting if any.
For the stopVideoRecording() method check if the success flag is true, if it's not then check the errorCode returned to see what that is. That will help to at least to diagnose an error.
I was playing around with the default generated code of viro cli, i was using Testbed App and i had updated my AndroidManifest.xml to ask for the permissions you mentioned above but it didn't work.
What i was doing is that i was placing a ViroButton with an image inside ViroARScene and on tap i had a function that was responsible for recording video but it wasn't being called as i had alerts being called inside it.
so i took the ref of the ViroARSceneNavigator in the App.js and called the startVideoRecording and other functions as mentioned in @se1exin comment and that worked, check the last code snippet.
// HelloWorldSceneAR.js
// this didn't work for me
// _onButtonTap wasn't being called for some reason
"use strict";
import React, { Component } from "react";
import { StyleSheet, PermissionsAndroid } from "react-native";
import { ViroARScene, ViroText, ViroConstants, ViroButton } from "react-viro";
export default class HelloWorldSceneAR extends Component {
constructor() {
super();
// Set initial state here
this.state = {
text: "Initializing AR...",
recording: false
};
// bind 'this' to functions
this._onInitialized = this._onInitialized.bind(this);
this._onButtonTap = this._onButtonTap.bind(this);
}
_onButtonTap() {
alert('_onButtonTap');
this.props.sceneNavigator.takeScreenshot("picpic", true);
}
render() {
return (
<ViroARScene onTrackingUpdated={this._onInitialized}>
<ViroText
text={this.state.text}
scale={[0.5, 0.5, 0.5]}
position={[0, 0, -1]}
style={styles.helloWorldTextStyle}
/>
<ViroButton
source={require("./res/guadalupe_360.jpg")}
position={[0, -5, -5]}
height={2}
width={3}
onTap={this._onButtonTap}
/>
</ViroARScene>
);
}
_onInitialized(state, reason) {
if (state == ViroConstants.TRACKING_NORMAL) {
this.setState({
text: "Hello World!"
});
} else if (state == ViroConstants.TRACKING_NONE) {
// Handle loss of tracking
}
}
}
var styles = StyleSheet.create({
helloWorldTextStyle: {
fontFamily: "Arial",
fontSize: 30,
color: "#ffffff",
textAlignVertical: "center",
textAlign: "center"
}
});
module.exports = HelloWorldSceneAR;
This worked for me
// App.js
// this worked for me
/**
* Copyright (c) 2017-present, Viro, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
import React, { Component } from 'react';
import {
AppRegistry,
Text,
View,
StyleSheet,
PixelRatio,
TouchableHighlight
} from 'react-native';
import { ViroVRSceneNavigator, ViroARSceneNavigator } from 'react-viro';
/*
TODO: Insert your API key below
*/
var sharedProps = {
apiKey: 'API_KEY'
};
// Sets the default scene you want for AR and VR
var InitialARScene = require('./js/HelloWorldSceneAR');
var InitialVRScene = require('./js/HelloWorldScene');
var UNSET = 'UNSET';
var VR_NAVIGATOR_TYPE = 'VR';
var AR_NAVIGATOR_TYPE = 'AR';
// This determines which type of experience to launch in, or UNSET, if the user should
// be presented with a choice of AR or VR. By default, we offer the user a choice.
var defaultNavigatorType = UNSET;
export default class ViroSample extends Component {
constructor() {
super();
this.state = {
navigatorType: defaultNavigatorType,
sharedProps: sharedProps,
recording: false
};
this._getExperienceSelector = this._getExperienceSelector.bind(this);
this._getARNavigator = this._getARNavigator.bind(this);
this._getVRNavigator = this._getVRNavigator.bind(this);
this._getExperienceButtonOnPress = this._getExperienceButtonOnPress.bind(
this
);
this._exitViro = this._exitViro.bind(this);
}
// componentDidMount() {
// this.requestCameraPermission();
// }
// requestCameraPermission = async () => {
// try {
// const readGranted = await PermissionsAndroid.request(
// PermissionsAndroid.READ_EXTERNAL_STORAGE,
// {
// title: "Allow permission to read",
// message: "Need access to storage."
// }
// );
// if (readGranted === PermissionsAndroid.RESULTS.GRANTED) {
// console.log("You can use the camera");
// } else {
// console.log("Camera permission denied");
// }
// const writeGranted = await PermissionsAndroid.request(
// PermissionsAndroid.WRITE_EXTERNAL_STORAGE,
// {
// title: "Allow permission to write",
// message: "Need access to storage."
// }
// );
// if (writeGranted === PermissionsAndroid.RESULTS.GRANTED) {
// console.log("You can use the camera");
// } else {
// console.log("Camera permission denied");
// }
// } catch (err) {
// console.warn(err);
// }
// };
// Replace this function with the contents of _getVRNavigator() or _getARNavigator()
// if you are building a specific type of experience.
render() {
if (this.state.navigatorType == UNSET) {
return this._getExperienceSelector();
} else if (this.state.navigatorType == VR_NAVIGATOR_TYPE) {
return this._getVRNavigator();
} else if (this.state.navigatorType == AR_NAVIGATOR_TYPE) {
return this._getARNavigator();
}
}
// Presents the user with a choice of an AR or VR experience
_getExperienceSelector() {
return (
<View style={localStyles.outer}>
<View style={localStyles.inner}>
<Text style={localStyles.titleText}>
Choose your desired experience:
</Text>
<TouchableHighlight
style={localStyles.buttons}
onPress={this._getExperienceButtonOnPress(AR_NAVIGATOR_TYPE)}
underlayColor={'#68a0ff'}
>
<Text style={localStyles.buttonText}>AR</Text>
</TouchableHighlight>
<TouchableHighlight
style={localStyles.buttons}
onPress={this._getExperienceButtonOnPress(VR_NAVIGATOR_TYPE)}
underlayColor={'#68a0ff'}
>
<Text style={localStyles.buttonText}>VR</Text>
</TouchableHighlight>
</View>
</View>
);
}
record = () => {
this.setState({ recording: !this.state.recording }, async () => {
const date = new Date();
const dateStr = `${date.getDate()}-${date.getMonth() +
1}-${date.getFullYear()} ${date.getHours()}_${date.getMinutes()}_${date.getSeconds()}`;
if (this.state.recording)
this.ARSceneNav.sceneNavigator.startVideoRecording(dateStr, true, err =>
alert(err)
);
else {
await this.ARSceneNav.sceneNavigator.stopVideoRecording();
}
});
// this.ARSceneNav.sceneNavigator.takeScreenshot("picture", true);
// alert("TOUCHED!");
};
// Returns the ViroARSceneNavigator which will start the AR experience
_getARNavigator() {
return (
<View style={{ flex: 1 }}>
<ViroARSceneNavigator
ref={ARSceneNav => (this.ARSceneNav = ARSceneNav)}
{...this.state.sharedProps}
initialScene={{ scene: InitialARScene }}
/>
<TouchableHighlight
style={{
padding: 10,
position: 'absolute',
left: '48%',
top: '48%',
backgroundColor: 'tomato'
}}
onPress={this.record}
>
<Text>{this.state.recording ? 'Stop' : 'Record'}</Text>
</TouchableHighlight>
</View>
);
}
// Returns the ViroSceneNavigator which will start the VR experience
_getVRNavigator() {
return (
<ViroVRSceneNavigator
{...this.state.sharedProps}
initialScene={{ scene: InitialVRScene }}
onExitViro={this._exitViro}
/>
);
}
// This function returns an anonymous/lambda function to be used
// by the experience selector buttons
_getExperienceButtonOnPress(navigatorType) {
return () => {
this.setState({
navigatorType: navigatorType
});
};
}
// This function "exits" Viro by setting the navigatorType to UNSET.
_exitViro() {
this.setState({
navigatorType: UNSET
});
}
}
var localStyles = StyleSheet.create({
viroContainer: {
flex: 1,
backgroundColor: 'black'
},
outer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'black'
},
inner: {
flex: 1,
flexDirection: 'column',
alignItems: 'center',
backgroundColor: 'black'
},
titleText: {
paddingTop: 30,
paddingBottom: 20,
color: '#fff',
textAlign: 'center',
fontSize: 25
},
buttonText: {
color: '#fff',
textAlign: 'center',
fontSize: 20
},
buttons: {
height: 80,
width: 150,
paddingTop: 20,
paddingBottom: 20,
marginTop: 10,
marginBottom: 10,
backgroundColor: '#68a0cf',
borderRadius: 10,
borderWidth: 1,
borderColor: '#fff'
},
exitButton: {
height: 50,
width: 100,
paddingTop: 10,
paddingBottom: 10,
marginTop: 10,
marginBottom: 10,
backgroundColor: '#68a0cf',
borderRadius: 10,
borderWidth: 1,
borderColor: '#fff'
}
});
module.exports = ViroSample;
Take a look at the Figment AR source code -> https://github.com/viromedia/figment-ar
This app includes both photo and video functionality
Most helpful comment
Yes, you can record and capture photos/video with AR and save them. See the async takeScreenshot and startVideoRecording methods here -> http://docs.viromedia.com/docs/viroarscenenavigator#methods