React Native相机问题:相机未正确显示- React-Native-Vision-Camera

kxeu7u2r  于 6个月前  发布在  React
关注(0)|答案(2)|浏览(145)

我希望你做得很好。我在我的React Native应用程序中遇到了一个与相机集成相关的特殊问题。下面是我所面临的问题的详细解释:

问题描述:我正在使用React Native构建一个视频录制应用程序,用户可以在前置和后置摄像头之间切换。然而,我遇到了一个问题,在某些情况下摄像头无法正常显示在屏幕上。

(点击链接查看视频中的问题):https://streamable.com/gu67vj

以下是问题的具体内容

1.摄像头关闭:打开应用时摄像头不显示,尤其是在初始加载时。只有当我强制重新渲染组件时(例如最小化应用并重新打开),摄像头才开始工作。
1.Key方法:我尝试使用key属性强制重新渲染摄像头组件,但没有完全解决问题。

附加信息
*相机包:我使用react-native-vision-camera进行相机集成。
*错误:已正确授予摄像头和麦克风权限并记录。
*测试:我尝试了不同的方法,包括使用key属性,但问题仍然存在。
*软件包版本

{ "@react-native-camera-roll/camera-roll": "^6.0.0", "react": "18.2.0", "react-native": "0.72.6", "react-native-vector-icons": "^10.0.0", "react-native-video": "^5.2.1", "react-native-vision-camera": "^3.6.4" }

相关编码

import React, { useState, useEffect, useRef } from 'react';
import {
  StyleSheet,
  StatusBar,
  View,
  Text,
  TouchableOpacity,
  ActivityIndicator,
  SafeAreaView,
  Alert,
  ToastAndroid,
  Linking,
} from 'react-native';

import {Camera, useCameraDevice} from 'react-native-vision-camera';

import Video from 'react-native-video';

import {CameraRoll} from '@react-native-camera-roll/camera-roll';
import Share from 'react-native-share';

import Icon from 'react-native-vector-icons/Ionicons';

function App() {
  const [isLoading, setIsLoading] = useState(true);
  const [isRecording, setIsRecording] = useState(false);
  const [recordingDuration, setRecordingDuration] = useState(0);
  const [isMuted, setIsMuted] = useState(false);

  const [isFlashOn, setIsFlashOn] = useState(false);
  const [isFrontCamera, setIsFrontCamera] = useState(true);

  const [recordedVideo, setRecordedVideo] = useState();

  StatusBar.setBackgroundColor('#3CF33A');
  const device = useCameraDevice(isFrontCamera ? 'front' : 'back');
  const camera = useRef(null);

  useEffect(() => {
    async function getPermissions() {
      const cameraPermission = await Camera.requestCameraPermission();
      const microphonePermission = await Camera.requestMicrophonePermission();

      console.log(`Camera Permission Status: ${cameraPermission}`);
      console.log(`Microphone Permission Status: ${microphonePermission}`);

      if (cameraPermission === 'denied' || microphonePermission === 'denied') {
        await Linking.openSettings();
      }

      setIsLoading(false);
    }

    getPermissions();
  }, []);

  useEffect(() => {
    let interval;
    if (isRecording) {
      interval = setInterval(() => {
        setRecordingDuration(prevDuration => prevDuration + 1);
      }, 1000);
    } else {
      clearInterval(interval);
    }

    return () => {
      clearInterval(interval);
    };
  }, [isRecording]);

  if (isLoading) {
    return (
      <ActivityIndicator
        size="large"
        color="#3CF33A"
        style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}
      />
    );
  }

  if (device == null)
    return (
      <ActivityIndicator
        size="large"
        color="#3CF33A"
        style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}
      />
    );

  const toggleFlash = () => {
    setIsFlashOn(prevState => !prevState);
  };

  const toggleCamera = () => {
    setIsFrontCamera(prevState => !prevState);
  };

  const handleStartRecording = async () => {
    try {
      if (camera.current) {
        setIsRecording(true);
        ToastAndroid.show('Recording started', ToastAndroid.SHORT);
        camera.current.startRecording({
          flash: isFlashOn ? 'on' : 'off',
          onRecordingFinished: video => {
            setRecordedVideo(video);
            console.log(video);
          },
          onRecordingError: error => console.error(error),
        });
      }
    } catch (error) {
      console.log(error);
    }
  };

  const formatTime = seconds => {
    const minutes = Math.floor(seconds / 60);
    const remainingSeconds = seconds % 60;
    return `${String(minutes).padStart(2, '0')}:${String(
      remainingSeconds,
    ).padStart(2, '0')}`;
  };

  const handleStopRecording = async () => {
    try {
      if (camera.current) {
        setIsRecording(false);
        setRecordingDuration(0);
        ToastAndroid.show('Recording stopped', ToastAndroid.SHORT);
        await camera.current.stopRecording();
      }
    } catch (error) {
      console.log(error);
    }
  };

  if (recordedVideo) {
    const handleSaveVideo = async () => {
      try {
        if (recordedVideo && recordedVideo.path) {
          await CameraRoll.save(recordedVideo.path, {
            type: 'video',
            album: 'Secrate Face Changer',
          });
          ToastAndroid.show('Video saved to gallery', ToastAndroid.SHORT);
        } else {
          ToastAndroid.show('No video to save', ToastAndroid.SHORT);
        }
      } catch (error) {
        console.error(error);
        ToastAndroid.show('Failed to save video', ToastAndroid.SHORT);
      }
    };

    const handleShareVideo = async () => {
      try {
        if (recordedVideo && recordedVideo.path) {
          const options = {
            title: 'Share Video',
            message: 'Check out this Video Recorded with Secret Face Changer!',
            url: `file://${recordedVideo.path}`,
            type: 'video/mp4',
          };
          await Share.open(options);
          ToastAndroid.show('Video Shared', ToastAndroid.SHORT);
        } else {
          ToastAndroid.show('No video to share', ToastAndroid.SHORT);
        }
      } catch (error) {
        console.log(error);
      }
    };

    const handleDiscardVideo = () => {
      Alert.alert(
        'Discard Video',
        'Are you sure you want to discard this video?',
        [
          {
            text: 'Cancel',
            style: 'cancel',
          },
          {
            text: 'Discard',
            onPress: () => {
              ToastAndroid.show('Video discarded', ToastAndroid.SHORT);
              setRecordedVideo(null);
            },
          },
        ],
        {cancelable: false},
      );
    };

    const toggleMute = () => {
      setIsMuted(prevState => !prevState);
    };

    return (
      <SafeAreaView style={{flex: 1}}>
        <Video
          source={{uri: recordedVideo.path}}
          ref={ref => {
            this.player = ref;
          }}
          onBuffer={this.onBuffer}
          onError={this.videoError}
          repeat={true}
          muted={isMuted}
          controls={true}
          resizeMode="cover"
          style={StyleSheet.absoluteFill}
        />

        <TouchableOpacity
          onPress={handleDiscardVideo}
          style={{top: 20, left: 10}}>
          <Icon name="trash" size={30} color="white" />
        </TouchableOpacity>

        <TouchableOpacity
          style={{position: 'absolute', top: 20, right: 20}}
          onPress={toggleMute}>
          <Icon
            name={isMuted ? 'volume-mute' : 'volume-high'}
            size={30}
            color="white"
          />
        </TouchableOpacity>

        <TouchableOpacity
          onPress={handleSaveVideo}
          style={{position: 'absolute', top: 60, right: 20}}>
          <Icon name="download" size={30} color="white" />
        </TouchableOpacity>

        <TouchableOpacity
          onPress={handleShareVideo}
          style={{position: 'absolute', top: 100, right: 20}}>
          <Icon name="share-social" size={30} color="white" />
        </TouchableOpacity>
      </SafeAreaView>
    );
  }

  return (
    <View style={{flex: 1}}>
      <Camera
        style={StyleSheet.absoluteFill}
        ref={camera}
        device={device}
        isActive={true}
        video={true}
        audio={true}
        videoQuality="1080p"
      />

      {isRecording ? (
        <View style={styles.recordingTimer}>
          <Text style={{color: 'white'}}>{formatTime(recordingDuration)}</Text>
        </View>
      ) : null}

      {!isFrontCamera && !isRecording ? (
        <TouchableOpacity onPress={toggleFlash} style={{top: 20, left: 10}}>
          <Icon
            name={isFlashOn ? 'flash' : 'flash-off'}
            size={34}
            color="white"
          />
        </TouchableOpacity>
      ) : null}

      <TouchableOpacity onPress={toggleCamera} style={styles.cameraToggleBtn}>
        <Icon
          name={isFrontCamera ? 'camera-reverse' : 'camera'}
          size={34}
          color="white"
        />
      </TouchableOpacity>

      {!isRecording ? (
        <TouchableOpacity
          style={styles.recordBtn}
          onPress={handleStartRecording}></TouchableOpacity>
      ) : (
        <TouchableOpacity
          style={styles.recordingBtn}
          onPress={handleStopRecording}></TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  recordBtn: {
    width: 70,
    height: 70,
    borderRadius: 35,
    backgroundColor: '#3cf33a',
    position: 'absolute',
    alignSelf: 'center',
    bottom: 30,
    elevation: 10,
  },
  recordingTimer: {
    alignSelf: 'center',
    alignItems: 'center',
    justifyContent: 'center',
    width: 80,
    height: 40,
    fontSize: 15,
    padding: 10,
    backgroundColor: 'red',
    elevation: 10,
    position: 'absolute',
    bottom: 100,
    borderRadius: 25,
  },
  recordingBtn: {
    width: 50,
    height: 50,
    borderRadius: 10,
    backgroundColor: 'red',
    position: 'absolute',
    alignSelf: 'center',
    bottom: 30,
  },
  cameraToggleBtn: {
    position: 'absolute',
    top: 20,
    right: 20,
  },
});

export default App;

字符串

qgzx9mmu

qgzx9mmu1#

在状态初始化期间使用useCameraDevices内的摄像机状态。
这段代码是为我工作,请尝试与这个例子

import React, {useEffect, useState, useRef} from 'react';
import {
  View,
  StyleSheet,
  Button,
  TouchableOpacity,
  Text,
  Linking,
  Image,
} from 'react-native';
import {Camera,  useCameraDevice,  useCameraDevices} from 'react-native-vision-camera';

function App() {
  const [cameraPosition, setCameraPosition] = useState('back')
  const [showCamera, setShowCamera] = useState(true);
  const [imageSource, setImageSource] = useState('');

  const devices = useCameraDevice(cameraPosition);
  const camera = useRef(null);

  useEffect(() => {
    async function getPermission() {
      const newCameraPermission = await Camera.requestCameraPermission();
      console.log(newCameraPermission);
    }
    getPermission();
  }, []);

  const capturePhoto = async () => {
    if (camera.current !== null) {
      const photo = await camera.current.takePhoto({});
      setImageSource(photo.path);
      setShowCamera(false);
      console.log(photo.path);
    }
  };

  if (devices == null) {
    return <Text>Camera not available</Text>;
  }

  return (
    <View style={styles.container}>
      {showCamera ? (
        <>
          <Camera
            ref={camera}
            style={StyleSheet.absoluteFill}
            device={devices}
            isActive={showCamera}
            photo={true}
          />

          <View style={styles.buttonContainer}>
            <TouchableOpacity
              style={styles.camButton}
              onPress={() => capturePhoto()}
            />
          </View>
        </>
      ) : (
        <>
          {imageSource !== '' ? (
            <Image
              style={styles.image}
              source={{
                uri: `file://'${imageSource}`,
              }}
            />
          ) : null}

          <View style={styles.backButton}>
            <TouchableOpacity
              style={{
                backgroundColor: 'rgba(0,0,0,0.2)',
                padding: 10,
                justifyContent: 'center',
                alignItems: 'center',
                borderRadius: 10,
                borderWidth: 2,
                borderColor: '#fff',
                width: 100,
              }}
              onPress={() => setShowCamera(true)}>
              <Text style={{color: 'white', fontWeight: '500'}}>Back</Text>
            </TouchableOpacity>
          </View>
          <View style={styles.buttonContainer}>
            <View style={styles.buttons}>
              <TouchableOpacity
                style={{
                  backgroundColor: '#fff',
                  padding: 10,
                  justifyContent: 'center',
                  alignItems: 'center',
                  borderRadius: 10,
                  borderWidth: 2,
                  borderColor: '#77c3ec',
                }}
                onPress={() => setShowCamera(true)}>
                <Text style={{color: '#77c3ec', fontWeight: '500'}}>
                  Retake
                </Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={{
                  backgroundColor: '#77c3ec',
                  padding: 10,
                  justifyContent: 'center',
                  alignItems: 'center',
                  borderRadius: 10,
                  borderWidth: 2,
                  borderColor: 'white',
                }}
                onPress={() => setShowCamera(true)}>
                <Text style={{color: 'white', fontWeight: '500'}}>
                  Use Photo
                </Text>
              </TouchableOpacity>
            </View>
          </View>
        </>
      )}
    </View>
  );
}

export default App;
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  button: {
    backgroundColor: 'gray',
  },
  backButton: {
    backgroundColor: 'rgba(0,0,0,0.0)',
    position: 'absolute',
    justifyContent: 'center',
    width: '100%',
    top: 0,
    padding: 20,
  },
  buttonContainer: {
    backgroundColor: 'rgba(0,0,0,0.2)',
    position: 'absolute',
    justifyContent: 'center',
    alignItems: 'center',
    width: '100%',
    bottom: 0,
    padding: 20,
  },
  buttons: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    width: '100%',
  },
  camButton: {
    height: 80,
    width: 80,
    borderRadius: 40,
    //ADD backgroundColor COLOR GREY
    backgroundColor: '#B2BEB5',

    alignSelf: 'center',
    borderWidth: 4,
    borderColor: 'white',
  },
  image: {
    width: '100%',
    height: '100%',
    aspectRatio: 9 / 16,
  },
});

字符串

falq053o

falq053o2#

作为react-native-vision-camera文档的例子,你应该在渲染之前检查相机是否可用:

const device = useCameraDevice('back')

if (device == null) return <NoCameraErrorView />

return (
  <Camera
    style={StyleSheet.absoluteFill}
    device={device}
    isActive={true}
  />
)

字符串

相关问题