question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Regression: After upgrading to SDK42, Camera recordAsync with maxDuration doesn't work with quality 720p specified on iOS

See original GitHub issue

Summary

After upgrading to SDK 42, Camera’s recordAsync({maxDuration: 5, quality: Camera.Constants.VideoQuality['720p']}) no longer works on iOS: recordAsync returns immediately and although it returns with a .mov file name, FileSystem.getInfoAsync returns that the file doesn’t exist. Commenting out the quality option resolves the issue.

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

managed

What platform(s) does this occur on?

iOS

SDK Version (managed workflow only)

42

Environment

% expo diagnostics

  Expo CLI 4.7.3 environment info:
    System:
      OS: macOS 11.4
      Shell: 5.8 - /bin/zsh
    Binaries:
      Node: 14.17.3 - /usr/local/bin/node
      Yarn: 1.22.10 - /usr/local/bin/yarn
      npm: 6.14.13 - /usr/local/bin/npm
      Watchman: 2021.06.07.00 - /usr/local/bin/watchman
    Managers:
      CocoaPods: 1.10.1 - /usr/local/bin/pod
    SDKs:
      iOS SDK:
        Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4
    IDEs:
      Xcode: 12.5.1/12E507 - /usr/bin/xcodebuild
    npmPackages:
      expo: ~42.0.1 => 42.0.1 
      react: 16.13.1 => 16.13.1 
      react-dom: 16.13.1 => 16.13.1 
      react-native: https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz => 0.63.2 
      react-native-web: ~0.13.12 => 0.13.18 
    npmGlobalPackages:
      expo-cli: 4.7.3
    Expo Workflow: managed

Reproducible demo or steps to reproduce from a blank project

Minimal reproducible demo: https://github.com/kevgrig/exposdk42vidissue App.js reproduced below. Steps:

  1. Launch App
  2. Click Record. Console shows that this.cameraRef.recordAsync returns immediately with a mov file that doesn’t exist:
    TakeVideoScreen.startRecording maxDuration: 5
    TakeVideoScreen.startRecording returned: [...]]CA6F553ECB0E.mov
    File doesn't exist
    
  3. Change const USE_QUALITY = true; to const USE_QUALITY = false;
  4. Click Record and it works as expected with this.cameraRef.recordAsync returning after 5 seconds and the mov file exists:
    TakeVideoScreen.startRecording maxDuration: 5
    onRecordingInterval 5
    onRecordingInterval 4
    onRecordingInterval 3
    onRecordingInterval 2
    onRecordingInterval 1
    TakeVideoScreen.startRecording returned: [...]]3A417EB33340.mov
    TakeVideoScreen.startRecording bytes: 5147387, uri: [...]]3A417EB33340.mov
    

App.js:

import React from 'react';
import { Button, Dimensions, Text, View } from 'react-native';
import { Audio } from 'expo-av';
import { Camera } from 'expo-camera';
import * as FileSystem from 'expo-file-system';

const MAX_DURATION = 5;
const USE_QUALITY = false;

class TakeVideoScreen extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      hasCameraPermission: false,
      timeLeft: MAX_DURATION,
    };
  }

  async componentDidMount() {
    try {
      const { status: cameraStatus } = await Camera.requestPermissionsAsync();
      const { status: audioStatus } = await Audio.requestPermissionsAsync();
      this.setState({ hasCameraPermission: cameraStatus === "granted" && audioStatus === "granted" });
    } catch (e) {
      console.log(e);
    }
  }

  onRecordingInterval = () => {
    console.log("onRecordingInterval " + this.state.timeLeft);
    this.setState((state, props) => {
      let timeLeft = state.timeLeft - 1;
      if (timeLeft < 0) {
        timeLeft = 0;
      }
      return { timeLeft: timeLeft };
    });
  }

  startRecording = async () => {
    if (!this.isRecording) {
      try {
        this.isRecording = true;

        if (this.intervalId) {
          clearInterval(this.intervalId);
        }
  
        console.log("TakeVideoScreen.startRecording maxDuration: " + this.state.timeLeft);
  
        this.intervalId = setInterval(this.onRecordingInterval, 1000);
  
        const options = {
          maxDuration: MAX_DURATION,
        };
        
        if (USE_QUALITY) {
          options.quality = Camera.Constants.VideoQuality['720p']
        }
        
        const data = await this.cameraRef.recordAsync(options);
  
        console.log("TakeVideoScreen.startRecording returned: " + data.uri);
  
        clearInterval(this.intervalId);
  
        const fileInfo = await FileSystem.getInfoAsync(data.uri);
        if (fileInfo.exists) {
  
          const numBytes = fileInfo.size;
  
          console.log("TakeVideoScreen.startRecording bytes: " + numBytes + ", uri: " + data.uri);
  
        } else {
          console.log("File doesn't exist")
        }

        this.setState({ timeLeft: MAX_DURATION });

      } catch (e) {
        console.log("Error: " + e);
      } finally {
        this.isRecording = false;
      }
    }
  }

  render() {
    if (this.state.hasCameraPermission === null) {
      // Asking for permissions
      return <View />;
    } else if (this.state.hasCameraPermission === false) {
      return (
        <Text>No Camera</Text>
      );
    } else {
      const { width, height } = Dimensions.get("window");
      return (
        <View style={{ flex: 1, paddingTop: 40, paddingBottom: 40 }}>
          <Button title="Record" onPress={this.startRecording} />
          <Text style={{ alignSelf: "center", padding: 20 }}>Time left: {this.state.timeLeft} seconds</Text>
          <Camera
            style={{ flex: 1, width: width - 40 }}
            type={Camera.Constants.Type.front}
            ref={camera => this.cameraRef = camera}
          />
        </View>
      );
    }
  }
}

export default function App() {
  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <TakeVideoScreen />
    </View>
  );
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
ajsmthcommented, Jul 19, 2021

@kevgrig thank you for putting together this repro! really helps a lot! a fix for this is coming soon 👍

0reactions
ajsmthcommented, Sep 15, 2021

hey there - we should be releasing sdk 43 within the next few weeks. otherwise you’d have to eject your app to get the updates. for future reference, you can check the changeling of a package to see if its available: https://github.com/expo/expo/blob/master/packages/expo-camera/CHANGELOG.md

in this case, there are native code changes which means a new native build is required. we usually have a beta period before releasing a new sdk which would let you try out these changes sooner than the official release date, so I would also keep an eye out for that!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Camera
Camera. expo-camera provides a React component that renders a preview for the device's front or back camera. The camera's parameters like zoom, auto...
Read more >
Unable to save video anymore with Expo on iOS
Issue is create by camera.recordAsync() that stops almost as soon as it starts. Only happens when the quality prop is used.
Read more >
[RESOLVED] Using .recordAsync and .stopRecording ...
I've tried testing on my phone and it's not working but I don't have a great ... { quality: '720p', maxDuration: 60 };...
Read more >
Changelog
[Android] Core: Fix a bug where the camera preview would not be centered on screen when setting setPreviewAspectFill to true on the ScanFragment...
Read more >
Expo SDK 41
Today we're announcing the release of Expo SDK 41. SDK 41 includes React Native 0.63, the same vers... Tagged with mobile, reactnative, ios, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found