Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I capture photo with Viro AR? #75

Closed
dam00n opened this issue Oct 30, 2017 · 15 comments
Closed

Can I capture photo with Viro AR? #75

dam00n opened this issue Oct 30, 2017 · 15 comments

Comments

@dam00n
Copy link
Collaborator

dam00n commented Oct 30, 2017

I've just skimmed through documents and got a question.
Can I record or capture photo/video with AR and save them?

@dam00n
Copy link
Collaborator Author

dam00n commented Oct 30, 2017

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

@dam00n dam00n closed this as completed Dec 8, 2017
@se1exin
Copy link

se1exin commented Sep 29, 2018

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:

  1. 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

@manbod
Copy link
Collaborator

manbod commented Oct 1, 2018

@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.

@Noor0
Copy link

Noor0 commented Jan 3, 2019

i am using this.props.sceneNavigator.startVideoRecording, this.props.sceneNavigator.stopVideoRecording & takeScreenshot but it's not saving the video or picture on my phone

@VikAdvani
Copy link
Contributor

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.

@Noor0
Copy link

Noor0 commented Jan 4, 2019

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;

@dam00n
Copy link
Collaborator Author

dam00n commented Jan 7, 2019

Take a look at the Figment AR source code -> https://github.com/viromedia/figment-ar

This app includes both photo and video functionality

@Wildanzr
Copy link

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

@astrolinux
Copy link

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

The same for me! Has anyone solved it?

@netanelbi
Copy link

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

The same for me! Has anyone solved it?

The same for me, anyone solved and can help ?
Thank you

@Wildanzr
Copy link

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

The same for me! Has anyone solved it?

The same for me, anyone solved and can help ? Thank you

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

Ended up using react-native-view-show

const handleScreenshot = async (): Promise<void> => {
    try {
      await logAnalyticsEvent("take_ar_screenshot");
      const randomName = Math.random().toString(36).substring(7);
      const localUri = await captureRef(viroNavigatorRef, {
        format: "jpg",
        quality: 1,
        fileName: randomName,
        handleGLSurfaceViewOnAndroid: true, // Make sure to enable this so you don't get blank picture
      });
      setImagePreview(localUri);
    } catch (error) {
      console.error("Cant take screenshot", error);
      console.log(error);
    }
  };

@netanelbi
Copy link

How come this work ?

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

The same for me! Has anyone solved it?

The same for me, anyone solved and can help ? Thank you

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

Ended up using react-native-view-show

const handleScreenshot = async (): Promise<void> => {
    try {
      await logAnalyticsEvent("take_ar_screenshot");
      const randomName = Math.random().toString(36).substring(7);
      const localUri = await captureRef(viroNavigatorRef, {
        format: "jpg",
        quality: 1,
        fileName: randomName,
        handleGLSurfaceViewOnAndroid: true, // Make sure to enable this so you don't get blank picture
      });
      setImagePreview(localUri);
    } catch (error) {
      console.error("Cant take screenshot", error);
      console.log(error);
    }
  };

How come this work ? I actually need the recording capabilities so I can't use something else. Can we record the scene with a different module or plugin ?

@Wildanzr
Copy link

How come this work ?

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

The same for me! Has anyone solved it?

The same for me, anyone solved and can help ? Thank you

On API level 33, permissions WRITE_EXTERNAL_STORAGE is already granted on app directory by default. But the result of function _takeScreenshot is not success and return {"errorCode": 1, "success": false, "url": null}. As in ViroConstant docs, errorCode 1 is stands for RECORD_ERROR_NO_PERMISSION. So how to tackle this?

Ended up using react-native-view-show

const handleScreenshot = async (): Promise<void> => {
    try {
      await logAnalyticsEvent("take_ar_screenshot");
      const randomName = Math.random().toString(36).substring(7);
      const localUri = await captureRef(viroNavigatorRef, {
        format: "jpg",
        quality: 1,
        fileName: randomName,
        handleGLSurfaceViewOnAndroid: true, // Make sure to enable this so you don't get blank picture
      });
      setImagePreview(localUri);
    } catch (error) {
      console.error("Cant take screenshot", error);
      console.log(error);
    }
  };

How come this work ? I actually need the recording capabilities so I can't use something else. Can we record the scene with a different module or plugin ?

Unfortunately, I haven't found yet the solution for recording capabilities.
Basicly for taking picture you need to add ref inside ViroARSceneNavigator with the types of ViroARSceneNavigator, then you can use this ref and pass to captureRef function from react-native-view-shot.

const viroNavigatorRef = useRef<ViroARSceneNavigator | null>(null);

.....
       <>
          <ViroARSceneNavigator
            autofocus
            ref={viroNavigatorRef}
            initialScene={{
              scene: ARScene,
            }}
            videoQuality="High"
            style={{
              flex: 1,
              width: "100%",
              height: "100%",
            }}
          />

          <ActionBottom handleScreenshot={handleScreenshot} />
        </>
....

@netanelbi
Copy link

netanelbi commented Aug 15, 2024

i found that there is a pull request on viro-core that should fix this permission issue. it seems that the plugin itself returns false for the permissions even though it doesnt have to ask for them
Fixed permission related issues for the SDK

@thedentist89
Copy link

Hello, I'm trying to record video on android 14 and I'm having the same error code 1 (permissions issue), any way around it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants