react-native-vision-camera icon indicating copy to clipboard operation
react-native-vision-camera copied to clipboard

๐Ÿ› onRecordingFinished returns width: 0, height: 0 on every recording

Open ChristopherGabba opened this issue 1 year ago โ€ข 23 comments

What's happening?

No matter which camera I use (front or back) on my iPhone 12 physical device, I'm getting the width and height of 0.

const onRecordingFinished = useCallback(
    async (video: VideoFile) => {
      console.log("Finished recording with", JSON.stringify(video, null, 4))
      setVideoUrl(video.path)
    },
    [setVideoUrl],
  )
Finished recording with {
    "path": "file:///private/var/mobile/Containers/Data/Application/19DBB76D-4103-4B38-AC3A-C57B843D3189/tmp/14C46EA6-AD67-4E6B-88C2-C1BFCFE7C83D.mp4",
    "width": 0,
    "height": 0,
    "duration": 5.033449333
}

Reproduceable Code

import { observer } from "mobx-react-lite"
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react"
import { View, ViewStyle } from "react-native"
import { DraggableOverlay } from "./DraggableOverlay"
import { MediaPlayer } from "./MediaPlayer"
import { AppStackScreenProps } from "app/navigators"
import { aS, colors, layout } from "../../theme"
import {
  Camera,
  CameraCaptureError,
  CameraRuntimeError,
  VideoFile,
  useCameraDevice,
  useCameraFormat,
  useFrameProcessor,
} from "react-native-vision-camera"
import {
  Face,
  useFaceDetector,
  FaceDetectionOptions,
} from "react-native-vision-camera-face-detector"
import { Worklets } from "react-native-worklets-core"
import {
  useIsForeground,
  useTimeout,
  useVideoLoadingError,
  useRecordingVolumeManager,
} from "app/hooks"
import { SharedTransitionAndSwipeDownWrapper } from "app/screens/MediaScreens/SharedTransitionAndSwipeDownWrapper"
import { reportCrash } from "app/utils/crashReporting"
import { CountdownTimer } from "./CountdownTimer"
import { PlayerStates } from "./media.types"
import { TimerStatus } from "./timer.types"
import { VolumeManager } from "react-native-volume-manager"
import { useIsFocused } from "@react-navigation/native"
import { EndRecordingButton } from "./EndRecordingButton"
import { TimerBar } from "./TimerBar"
import { AVPlaybackStatus, Audio, Video } from "expo-av"
import { trackAnalytics } from "app/utils/analytics"
import * as Haptics from "expo-haptics"
import { SmileData, generateLaughData } from "app/utils/generateLaughData"
import { generateThumbnailFromVideo } from "app/utils/generateThumbnail"
import NetInfo from "@react-native-community/netinfo"
import { fixLocalPath } from "app/utils/commonFunctions"
import { BlurView } from "expo-blur"

interface RecordingProps extends AppStackScreenProps<"RecordingTest"> {}

export const RecordingTestScreen: FC<RecordingProps> = observer(function RecordingTestScreen(
  _props, // @demo remove-current-line
) {
  const { navigation } = _props
  // #region - initializations
  const cameraRef = useRef<Camera | null>(null)

  const [mediaPlayerReady, setMediaPlayerReady] = useState(false)
  const [cameraReady, setCameraReady] = useState(false)

  const faceDetectionOptions = useRef<FaceDetectionOptions>({
    // detection options
    classificationMode: "all",
  }).current
  const videoFrameCount = useRef(0)
  const [smilePeakTimestamp, setSmilePeakTimestamp] = useState<number>()
  const { detectFaces } = useFaceDetector(faceDetectionOptions)
  const smileData = useRef<SmileData[]>([])
  const isFocused = useIsFocused()
  const isForeground = useIsForeground()

  const isActive = useMemo(() => {
    return isFocused && isForeground
  }, [isFocused, isForeground])

  const device = useCameraDevice("back")
  const [targetFps] = useState(30)

  // console.log(JSON.stringify(device, (k, v) => k === "formats" ? [] : v, 2))
  const format = useCameraFormat(device, [
    { videoStabilizationMode: "standard" },
    { fps: targetFps },
    { videoAspectRatio: 16 / 9 },
    { videoResolution: { width: 308, height: 548 } },
    { photoAspectRatio: 16 / 9 },
    { photoResolution: { width: 308, height: 548 } },
  ])

  // #endregion

  const { isUsingHeadphones } = useRecordingVolumeManager({ isMuted: false })

  const [playStatus, setPlayStatus] = useState<PlayerStates>("unstarted")
  const [initialCountTimerStatus, setInitialCountdownTimerStatus] =
    useState<TimerStatus>("unstarted")

  const [videoUrl, setVideoUrl] = useState("")

  const [mediaError, setMediaError] = useState(false)
  const [cameraError, setCameraError] = useState<
    CameraCaptureError | CameraRuntimeError | unknown
  >()


  useEffect(() => {
    if (cameraReady && mediaPlayerReady) {
      setInitialCountdownTimerStatus("play")
    }
  }, [cameraReady, mediaPlayerReady])

  /**
   * This keeps track of the overall video duration as soon as
   * the play status is set to "play",
   * TODO: add automatic pause sequencing here on video buffer
   */
  useTimeout({
    initiate: playStatus === "playing",
    initialValue: 30,
    onCountdownComplete: () => {
      setPlayStatus("completed")
    },
  })

  useEffect(() => {
    if (!isForeground && !videoUrl) {
      cameraRef.current?.stopRecording()
      navigation.goBack()
    }
  }, [isForeground, videoUrl])

  const onPlayerReady = useCallback(() => {
    setMediaPlayerReady(true)
  }, [setMediaPlayerReady])

  const onRecordingFinished = useCallback(
    async (video: VideoFile) => {
      console.log("Finished recording with", JSON.stringify(video, null, 4))
      const { smilePeakTimestamp } = generateLaughData({
        smileData: smileData.current,
        frameRate: targetFps,
      })
      const recordedPath = fixLocalPath(video.path)
      const fallbackTimestamp = video.duration * 0.95
      const thumbnailTimestamp = Math.round(smilePeakTimestamp ?? fallbackTimestamp)

      const thumbnail = await generateThumbnailFromVideo(recordedPath, thumbnailTimestamp)

      if (!thumbnail) {
        setCameraError("Thumbnail Failure")
        return
      }
      setSmilePeakTimestamp(smilePeakTimestamp)
      setVideoUrl(video.path)
    },
    [setCameraError, setSmilePeakTimestamp, setVideoUrl],
  )

  /**
   * Begin the recording sequence and media playback
   */

  async function startEverything() {
    setInitialCountdownTimerStatus("stop")
    setPlayStatus("playing")
    await Audio.setAudioModeAsync({
      playsInSilentModeIOS: true,
    })
    cameraRef.current?.startRecording({
      fileType: "mp4",
      onRecordingFinished,
      onRecordingError: (error) => {
        setPlayStatus("stopped")
        setCameraError(error)
        alert(error)
      },
    })
  }

  /**
   * Callback to tell the camera to stop recording and
   * the video to stop playing
   */
  async function stopEverything() {
    try {
      setPlayStatus("stopped")
      await cameraRef.current?.stopRecording()
      await Audio.setAudioModeAsync({
        playsInSilentModeIOS: true,
        allowsRecordingIOS: false,
      })
      navigation.goBack()
    } catch (error) {
      reportCrash({ error, method: "stopEverything", screen: "Recording" })
    }
  }

  async function cancelEverything() {
    try {
      setPlayStatus("stopped")
      await cameraRef.current?.cancelRecording()
    } catch (error) {
      reportCrash({ error, method: "cancelEverything", screen: "Recording" })
    }
  }

  const onPlayerError = useCallback(
    async (error: string) => {
      setMediaError(true)
      cancelEverything()
      reportCrash({
        error,
        method: "onPlayerError",
        screen: "Recording",
      })
    },
    [setMediaError],
  )

  const onCameraError = useCallback(
    (error: CameraRuntimeError) => {
      Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)
      switch (error.code) {
        case "session/audio-in-use-by-other-app":
          {
            alert("The camera or microphone is in use by another app")
            setPlayStatus("stopped")
            navigation.goBack()
          }
          return
        default: {
          trackAnalytics({
            event: "Camera Error",
            properties: {
              Error: error.code,
            },
          })
          reportCrash({
            error,
            method: "onCameraError",
            screen: "Recording",
          })
        }
      }
    },
    [setPlayStatus],
  )

  const handlePlayerStateChange = useCallback(
    (e: AVPlaybackStatus) => {
      if (e.isLoaded) {
        if (e.isBuffering) {
          cameraRef.current?.pauseRecording()
        } else if (e.isPlaying) {
          cameraRef.current?.resumeRecording()
        }
      }
    },
    [cameraRef.current, initialCountTimerStatus],
  )

  const onPreviewStarted = useCallback(() => {
    setCameraReady(true)
  }, [setCameraReady])

  const handleDetectedFaces = Worklets.createRunOnJS((faces: Face[]) => {
    videoFrameCount.current++
    if (faces.length === 0) return
    smileData.current.push({
      frame: videoFrameCount.current,
      probability: faces[0].smilingProbability,
    })
  })

  const frameProcessor = useFrameProcessor(
    (frame) => {
      "worklet"
      const faces = detectFaces(frame)
      // ... chain frame processors
      // ... do something with frame
      handleDetectedFaces(faces)
    },
    [handleDetectedFaces],
  )

  const onSwipeDownComplete = useCallback(() => {
    navigation.goBack()
  }, [])

  return (
    <SharedTransitionAndSwipeDownWrapper
      reset={playStatus === "playing"}
      disabled={playStatus !== "unstarted"}
      onSwipeDownComplete={onSwipeDownComplete}
    >
      <View style={$container}>
        <View style={$mediaContainer}>
          <Video
            shouldPlay={playStatus === "playing"}
            style={{ width: "100%", height: "100%" }}
            source={{
              uri: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
            }}
            onError={onPlayerError}
            volume={isUsingHeadphones ? 0.6 : 1.0}
            onReadyForDisplay={onPlayerReady}
            onPlaybackStatusUpdate={handlePlayerStateChange}
          />
          <TimerBar durationInSeconds={30} shouldPlay={playStatus === "playing"} />
        </View>
        <EndRecordingButton
          onPress={stopEverything}
          disabled={playStatus === "unstarted" || playStatus === "stopped"}
        />

        <DraggableOverlay visible={!videoUrl}>
          {device && (
            <Camera
              ref={cameraRef}
              video={true}
              audio={true}
              lowLightBoost={device?.supportsLowLightBoost}
              photo={true}
              format={format}
              isActive={isActive}
              device={device}
              exposure={0}
              outputOrientation="preview"
              frameProcessor={frameProcessor}
              style={$camera}
              onPreviewStarted={onPreviewStarted}
              onError={onCameraError}
            />
          )}
        </DraggableOverlay>
      </View>
      {playStatus === "unstarted" && (
        <BlurView
          style={{
            position: "absolute",
            width: "100%",
            height: "100%",
            justifyContent: "center",
            alignItems: "center",
          }}
          intensity={85}
        >
          <CountdownTimer
            initialValue={3}
            status={initialCountTimerStatus}
            setStatus={setInitialCountdownTimerStatus}
            onTimerComplete={startEverything}
          />
        </BlurView>
      )}
    </SharedTransitionAndSwipeDownWrapper>
  )
})

const $finalCountdownContainer: ViewStyle = {
  position: "absolute",
  bottom: aS(195),
  right: aS(10),
}

const $container: ViewStyle = {
  flex: 1,
  width: "100%",
  height: "100%",
}

const $mediaContainer: ViewStyle = {
  flexBasis: "79%",
  justifyContent: "center",
  alignItems: "center",
  backgroundColor: colors.background,
  width: layout.screenWidth,
  height: layout.screenHeight,
  zIndex: 0,
}

const $camera: ViewStyle = {
  flex: 1,
  backgroundColor: "white",
}

Relevant log output

Looking up Frame Processor Plugin "detectFaces"...
Frame Processor Plugin "detectFaces" found! Initializing...
2024-08-03 17:23:04.233 ReelFeel[51789/0x16d5af000] [lvl=3] +[MLKITx_CCTClearcutUploader crashIfNecessary] Multiple instances of CCTClearcutUploader were instantiated. Multiple uploaders function correctly but have an adverse affect on battery performance due to lock contention.
Initialized TensorFlow Lite runtime.
INFO: Initialized TensorFlow Lite runtime.
Created TensorFlow Lite XNNPACK delegate for CPU.
INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
17:23:04.567: [info] ๐Ÿ“ธ VisionCamera.constantsToExport(): Found 5 initial Camera Devices.
๐ŸŸข Creating JS object for module 'ExpoVideoView'
CGAffineTransformInvert: singular matrix.
[E] <MMKV.cpp:985::getDouble> reach end, m_position: 2, m_size: 2
17:23:04.704: [info] ๐Ÿ“ธ VisionCamera.didSetProps(_:): Updating 26 props: [onInitialized, cameraId, enableBufferCompression, preview, onOutputOrientationChanged, onCodeScanned, onStarted, lowLightBoost, backgroundColor, isActive, isMirrored, onViewReady, onError, onStopped, video, onPreviewOrientationChanged, onPreviewStopped, enableFrameProcessor, onPreviewStarted, format, flex, audio, outputOrientation, exposure, photo, onShutter]
17:23:04.705: [info] ๐Ÿ“ธ VisionCamera.configurePreviewOrientation(_:): Updating Preview rotation: landscapeLeft...
17:23:04.705: [info] ๐Ÿ“ธ VisionCamera.configureOutputOrientation(_:): Updating Outputs rotation: landscapeLeft...
17:23:04.705: [info] ๐Ÿ“ธ VisionCamera.configure(_:): configure { ... }: Waiting for lock...
17:23:04.708: [info] ๐Ÿ“ธ VisionCamera.configure(_:): configure { ... }: Updating CameraSession Configuration... Difference(inputChanged: true, outputsChanged: true, videoStabilizationChanged: true, orientationChanged: true, formatChanged: true, sidePropsChanged: true, torchChanged: true, zoomChanged: true, exposureChanged: true, audioSessionChanged: true, locationChanged: true)
17:23:04.708: [info] ๐Ÿ“ธ VisionCamera.configureDevice(configuration:): Configuring Input Device...
17:23:04.708: [info] ๐Ÿ“ธ VisionCamera.configureDevice(configuration:): Configuring Camera com.apple.avfoundation.avcapturedevice.built-in_video:0...
17:23:04.723: [debug] ๐Ÿ“ธ VisionCamera.sensorOrientation: Sensor Orientation changed from landscapeLeft -> portrait
17:23:04.723: [info] ๐Ÿ“ธ VisionCamera.configurePreviewOrientation(_:): Updating Preview rotation: portrait...
17:23:04.723: [info] ๐Ÿ“ธ VisionCamera.configureOutputOrientation(_:): Updating Outputs rotation: portrait...
17:23:04.723: [info] ๐Ÿ“ธ VisionCamera.configureDevice(configuration:): Successfully configured Input Device!
17:23:04.723: [info] ๐Ÿ“ธ VisionCamera.configureOutputs(configuration:): Configuring Outputs...
17:23:04.723: [info] ๐Ÿ“ธ VisionCamera.configureOutputs(configuration:): Adding Photo output...
17:23:04.728: [info] ๐Ÿ“ธ VisionCamera.configureOutputs(configuration:): Adding Video Data output...
17:23:04.729: [info] ๐Ÿ“ธ VisionCamera.configurePreviewOrientation(_:): Updating Preview rotation: portrait...
17:23:04.729: [info] ๐Ÿ“ธ VisionCamera.configureOutputOrientation(_:): Updating Outputs rotation: portrait...
17:23:04.729: [info] ๐Ÿ“ธ VisionCamera.configureOutputs(configuration:): Successfully configured all outputs!
17:23:04.731: [info] ๐Ÿ“ธ VisionCamera.setTargetOutputOrientation(_:): Setting target output orientation from device to preview...
17:23:04.731: [info] ๐Ÿ“ธ VisionCamera.configureFormat(configuration:device:): Configuring Format (4224x2384 | [email protected] (ISO: 33.0..3168.0))...
17:23:04.732: [info] ๐Ÿ“ธ VisionCamera.configureFormat(configuration:device:): Successfully configured Format!
17:23:04.734: [info] ๐Ÿ“ธ VisionCamera.getPixelFormat(for:): Available Pixel Formats: ["420v", "420f", "BGRA", "&8v0", "&8f0", "&BGA"], finding best match... (pixelFormat="yuv", enableHdr={false}, enableBufferCompression={false})
17:23:04.734: [info] ๐Ÿ“ธ VisionCamera.getPixelFormat(for:): Using PixelFormat: 420f...
17:23:04.984: [info] ๐Ÿ“ธ VisionCamera.init(frame:session:): Preview Layer started previewing.
17:23:04.985: [info] ๐Ÿ“ธ VisionCamera.configure(_:): Beginning AudioSession configuration...
17:23:04.986: [info] ๐Ÿ“ธ VisionCamera.configureAudioSession(configuration:): Configuring Audio Session...
17:23:04.986: [info] ๐Ÿ“ธ VisionCamera.configureAudioSession(configuration:): Adding Audio input...
17:23:04.989: [info] ๐Ÿ“ธ VisionCamera.configure(_:): Beginning Location Output configuration...
17:23:04.999: [info] ๐Ÿ“ธ VisionCamera.configureAudioSession(configuration:): Adding Audio Data output...
17:23:05.001: [info] ๐Ÿ“ธ VisionCamera.configure(_:): Committed AudioSession configuration!
17:23:05.061: [info] ๐Ÿ“ธ VisionCamera.configure(_:): Finished Location Output configuration!
Main thread blocked by synchronous property query on not-yet-loaded property (NaturalSize) for HTTP(S) asset. This could have been a problem if this asset were being read from a slow network.
Main thread blocked by synchronous property query on not-yet-loaded property (PreferredTransform) for HTTP(S) asset. This could have been a problem if this asset were being read from a slow network.
[I] <libMMKV.mm:301::-[MMKV onMemoryWarning]> cleaning on memory warning mmkv.default
[I] <MMKV.cpp:307::clearMemoryCache> clearMemoryCache [mmkv.default]
[I] <MemoryFile.cpp:104::close> closing fd[0x10], /var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/Documents/mmkv/mmkv.default
WARNING: Logging before InitGoogleLogging() is written to STDERR
I0803 17:23:08.729192 1834676224 JSIExecutor.cpp:348] Memory warning (pressure level: TRIM_MEMORY_RUNNING_CRITICAL) received by JS VM, running a GC
tcp_input [C11.1.1.1:3] flags=[R] seq=2283506070, ack=0, win=0 state=LAST_ACK rcv_nxt=2283506071, snd_una=2703863518
tcp_input [C11.1.1.1:3] flags=[R] seq=2283506070, ack=0, win=0 state=LAST_ACK rcv_nxt=2283506071, snd_una=2703863518
tcp_input [C11.1.1.1:3] flags=[R] seq=2283506071, ack=0, win=0 state=LAST_ACK rcv_nxt=2283506071, snd_una=2703863518
tcp_input [C11.1.1.1:3] flags=[R] seq=2283506071, ack=0, win=0 state=CLOSED rcv_nxt=2283506071, snd_una=2703863518
tcp_output [C10.1.1.1:3] flags=[R.] seq=3961394724, ack=1667659849, win=2047 state=CLOSED rcv_nxt=1667659849, snd_una=3961394661
17:23:11.673: [info] ๐Ÿ“ธ VisionCamera.startRecording(options:onVideoRecorded:onError:): Starting Video recording...
17:23:11.682: [info] ๐Ÿ“ธ VisionCamera.startRecording(options:onVideoRecorded:onError:): Will record to temporary file: file:///private/var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/tmp/0AE474EB-D396-47BC-9585-40D0E1474418.mp4
17:23:11.683: [info] ๐Ÿ“ธ VisionCamera.init(url:fileType:metadataProvider:clock:orientation:completion:): Creating RecordingSession... (orientation: landscapeLeft)
17:23:11.685: [info] ๐Ÿ“ธ VisionCamera.startRecording(options:onVideoRecorded:onError:): Enabling Audio for Recording...
17:23:11.685: [info] ๐Ÿ“ธ VisionCamera.activateAudioSession(): Activating Audio Session...
17:23:11.707: [info] ๐Ÿ“ธ VisionCamera.updateCategory(_:mode:options:): Changing AVAudioSession category from AVAudioSessionCategoryPlayback -> AVAudioSessionCategoryPlayAndRecord
17:23:11.714: [info] ๐Ÿ“ธ VisionCamera.initializeAudioTrack(withSettings:format:): Initializing Audio AssetWriter with settings: ["AVEncoderBitRateStrategyKey": AVAudioBitRateStrategy_Variable, "AVSampleRateKey": 44100, "AVNumberOfChannelsKey": 1, "AVEncoderQualityForVBRKey": 91, "AVFormatIDKey": 1633772320, "AVEncoderBitRatePerChannelKey": 96000]
17:23:11.721: [info] ๐Ÿ“ธ VisionCamera.initializeAudioTrack(withSettings:format:): Initialized Audio AssetWriter.
17:23:11.742: [info] ๐Ÿ“ธ VisionCamera.recommendedVideoSettings(forOptions:): Getting recommended video settings for AVFileType(_rawValue: public.mpeg-4) file...
17:23:11.847: [info] ๐Ÿ“ธ VisionCamera.updateCategory(_:mode:options:): AVAudioSession category changed!
17:23:11.871: [info] ๐Ÿ“ธ VisionCamera.activateAudioSession(): Audio Session activated!
17:23:11.979: [info] ๐Ÿ“ธ VisionCamera.initializeVideoTrack(withSettings:): Initializing Video AssetWriter with settings: ["AVVideoWidthKey": 960, "AVVideoCompressionPropertiesKey": {
    AllowFrameReordering = 1;
    AllowOpenGOP = 1;
    AverageBitRate = 4727808;
    ExpectedFrameRate = 30;
    MaxAllowedFrameQP = 41;
    MaxKeyFrameIntervalDuration = 1;
    MinAllowedFrameQP = 15;
    MinimizeMemoryUsage = 1;
    Priority = 80;
    ProfileLevel = "HEVC_Main_AutoLevel";
    RealTime = 1;
    RelaxAverageBitRateTarget = 1;
}, "AVVideoCodecKey": hvc1, "AVVideoHeightKey": 540]
17:23:11.986: [info] ๐Ÿ“ธ VisionCamera.initializeVideoTrack(withSettings:): Initialized Video AssetWriter.
17:23:11.986: [info] ๐Ÿ“ธ VisionCamera.start(): Starting Asset Writer...
17:23:16.172: [info] ๐Ÿ“ธ VisionCamera.start(): Asset Writer started!
17:23:16.172: [info] ๐Ÿ“ธ VisionCamera.start(): Asset Writer session started at 301972.368540291.
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.start(): Requesting video timeline start at 301972.369260666...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.start(): Requesting audio timeline start at 301972.371443208...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.startRecording(options:onVideoRecorded:onError:): RecordingSesssion started in 4501.861ms!
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.pause(): Pausing video timeline at 301972.3714855...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.pause(): Pausing audio timeline at 301972.371496375...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.pause(): Pausing video timeline at 301972.37157425...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.pause(): Pausing audio timeline at 301972.371587333...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.371615916...
17:23:16.175: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.3716285...
17:23:16.181: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.378242125...
17:23:16.181: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.378295958...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.378313708...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.3783235...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.378338375...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.378347291...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.378455541...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.37849925...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.3785575...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.378593208...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming video timeline at 301972.378618291...
17:23:16.182: [info] ๐Ÿ“ธ VisionCamera.resume(): Resuming audio timeline at 301972.378713166...
17:23:16.183: [info] ๐Ÿ“ธ VisionCamera.isTimestampWithinTimeline(timestamp:): audio Timeline: First timestamp: 301972.2975208333
17:23:16.185: [info] ๐Ÿ“ธ VisionCamera.isTimestampWithinTimeline(timestamp:): video Timeline: First timestamp: 301972.347921333
17:23:16.199: [info] ๐Ÿ“ธ VisionCamera.stop(): Stopping Asset Writer with status "writing"...
17:23:16.199: [info] ๐Ÿ“ธ VisionCamera.stop(): Requesting video timeline stop at 301972.395569958...
17:23:16.199: [info] ๐Ÿ“ธ VisionCamera.stop(): Requesting audio timeline stop at 301972.395595375...
[I] <MMKV_IO.cpp:214::loadMetaInfoAndCheck> meta file [mmkv.default] has flag [0]
[I] <MemoryFile.cpp:98::open> open fd[0x1e], /var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/Documents/mmkv/mmkv.default
[I] <MemoryFile.cpp:203::mmap> mmap to address [0x119400000], oldPtr [0x0], [/var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/Documents/mmkv/mmkv.default]
[I] <MemoryFile_OSX.cpp:35::tryResetFileProtection> protection on [/var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/Documents/mmkv/mmkv.default] is NSFileProtectionCompleteUntilFirstUserAuthentication
[I] <MMKV_IO.cpp:84::loadFromFile> loading [mmkv.default] with 216708 actual size, file size 524288, InterProcess 0, meta info version:4
[I] <MMKV_IO.cpp:89::loadFromFile> loading [mmkv.default] with crc 98401342 sequence 582 version 4
[I] <MMKV_IO.cpp:133::loadFromFile> loaded [mmkv.default] with 7 key-values
17:23:16.248: [info] ๐Ÿ“ธ VisionCamera.markAsFinished(lastTimestamp:stopTimestamp:): Last timestamp arrived at 301972.414583083 (0.019013125 seconds after stop()) - video Timeline is now finished!
17:23:16.248: [debug] ๐Ÿ“ธ VisionCamera.markAsFinished(lastTimestamp:stopTimestamp:): 301972.369260666: โบ๏ธ Started
301972.3714855: โธ๏ธ Paused
301972.37157425: โธ๏ธ Paused
301972.371615916: โ–ถ๏ธ Resumed
301972.378242125: โ–ถ๏ธ Resumed
301972.378313708: โ–ถ๏ธ Resumed
301972.378338375: โ–ถ๏ธ Resumed
301972.378455541: โ–ถ๏ธ Resumed
301972.3785575: โ–ถ๏ธ Resumed
301972.378618291: โ–ถ๏ธ Resumed
301972.395569958: โน๏ธ Stopped
17:23:16.255: [info] ๐Ÿ“ธ VisionCamera.append(buffer:): Marking video track as finished - target duration: CMTime(value: 26178876, timescale: 1000000000, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0), actual duration: CMTime(value: 33200417, timescale: 1000000000, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0) (0.007021541 seconds longer than expected)
[E] <MMKV.cpp:985::getDouble> reach end, m_position: 2, m_size: 2
17:23:16.289: [info] ๐Ÿ“ธ VisionCamera.markAsFinished(lastTimestamp:stopTimestamp:): Last timestamp arrived at 301972.4041875 (0.008592125 seconds after stop()) - audio Timeline is now finished!
17:23:16.289: [debug] ๐Ÿ“ธ VisionCamera.markAsFinished(lastTimestamp:stopTimestamp:): 301972.371443208: โบ๏ธ Started
301972.371496375: โธ๏ธ Paused
301972.371587333: โธ๏ธ Paused
301972.3716285: โ–ถ๏ธ Resumed
301972.378295958: โ–ถ๏ธ Resumed
301972.3783235: โ–ถ๏ธ Resumed
301972.378347291: โ–ถ๏ธ Resumed
301972.37849925: โ–ถ๏ธ Resumed
301972.378593208: โ–ถ๏ธ Resumed
301972.378713166: โ–ถ๏ธ Resumed
301972.395595375: โน๏ธ Stopped
17:23:16.290: [info] ๐Ÿ“ธ VisionCamera.append(buffer:): Marking audio track as finished - target duration: CMTime(value: 24020042, timescale: 1000000000, flags: __C.CMTimeFlags(rawValue: 1), epoch: 0), actual duration: CMTime(value: 85201208, timescale: 1000000000, flags: __C.CMTimeFlags(rawValue: 3), epoch: 0) (0.061181166 seconds longer than expected)
17:23:16.305: [info] ๐Ÿ“ธ VisionCamera.finish(): Stopping AssetWriter with status "writing"...
17:23:16.309: [info] ๐Ÿ“ธ VisionCamera.finish(): Asset Writer session stopped at 301972.38112175.
17:23:16.323: [info] ๐Ÿ“ธ VisionCamera.finish(): Asset Writer finished writing!
17:23:16.323: [info] ๐Ÿ“ธ VisionCamera.startRecording(options:onVideoRecorded:onError:): RecordingSession finished with status completed.
17:23:16.324: [info] ๐Ÿ“ธ VisionCamera.deactivateAudioSession(): Deactivating Audio Session...
'Finished recording with', '{\n    "path": "file:///private/var/mobile/Containers/Data/Application/D91FE63C-1122-46D1-98E4-BA3FF7A9AF82/tmp/0AE474EB-D396-47BC-9585-40D0E1474418.mp4",\n    "width": 0,\n    "height": 0,\n    "duration": 0.033200417\n}'
๐ŸŸข Creating JS object for module 'ExpoVideoThumbnails'
17:23:16.333: [info] ๐Ÿ“ธ VisionCamera.deactivateAudioSession(): Audio Session deactivated!

Camera Device

{
  "position": "back",
  "hardwareLevel": "full",
  "maxZoom": 123.75,
  "id": "com.apple.avfoundation.avcapturedevice.built-in_video:0",
  "sensorOrientation": "portrait",
  "formats": [],
  "supportsFocus": true,
  "supportsRawCapture": false,
  "neutralZoom": 1,
  "physicalDevices": [
    "wide-angle-camera"
  ],
  "isMultiCam": false,
  "supportsLowLightBoost": false,
  "maxExposure": 8,
  "minFocusDistance": 12,
  "minExposure": -8,
  "name": "Back Camera",
  "hasFlash": true,
  "minZoom": 1,
  "hasTorch": true
}

Device

iPhone 12 Physical

VisionCamera Version

4.5.1

Can you reproduce this issue in the VisionCamera Example app?

Yes, I can reproduce the same issue in the Example app here

Additional information

ChristopherGabba avatar Aug 03 '24 22:08 ChristopherGabba

Guten Tag, Hans here.

It seems you are encountering a bug with onRecordingFinished returning width and height of 0. Please ensure that the camera settings and permissions are correctly configured in your application. It would be helpful to provide more details about your setup or any specific steps that lead to this issue.

Additionally, if you wish for more in-depth support from ze mrousavy and ze team, consider supporting the project by sponsoring. This helps keep ze project active and allows for quicker responses on issues.

Danke schรถn!

Note: If you think I made a mistake by closing this issue, please ping @mrousavy to take a look.

maintenance-hans[bot] avatar Aug 03 '24 22:08 maintenance-hans[bot]

@ChristopherGabba How can I reduce the size of a video that is 151 MB for a 1-minute recording? The bitRate setting does not seem to be working. https://github.com/mrousavy/react-native-vision-camera/issues/3113

vishalyad16 avatar Aug 04 '24 17:08 vishalyad16

I have provided my entire setup in the details above, not sure what else I could provide. I'm also following all the steps in the docs unless I missed something that I'm not seeing.

Maybe this is related a pre-defined video resolution in the camera format (useCameraFormat). In that case shouldn't this return the predefined resolution?

ChristopherGabba avatar Aug 04 '24 22:08 ChristopherGabba

I'm seeing the same after trying to upgrade from 4.3.2 -> latest.

Seems to be a bug introduced in 4.4.0, since that's where it starts to break for me, returning {width: 0, height: 0}.

Edit: Also appears to work on Android, just not iOS.

INFO {"format": {"autoFocusSystem": "phase-detection", "fieldOfView": 69.46971893310547, "maxFps": 60, "maxISO": 3264, "minFps": 1, "minISO": 34, "photoHeight": 2340, "photoWidth": 4160, "supportsDepthCapture": false, "supportsPhotoHdr": false, "supportsVideoHdr": false, "videoHeight": 540, "videoStabilizationModes": ["auto", "cinematic", "off", "standard", "cinematic-extended"], "videoWidth": 960}} INFO {"videoFile": {"duration": 1.666647041, "height": 0, "path": "file:///private/var/mobile/Containers/Data/Application/30BA39A7-BF93-4BE7-B19C-2EDF73B610B7/tmp/0AA41CE1-CFD4-49D5-9016-8EB9101FDAD3.mov", "width": 0}}

@mrousavy Sorry, it said about to ping you if we think this is still an issue.

stewartiee avatar Aug 06 '24 14:08 stewartiee

sorry the bot probably shouldn't have closed this.

thanks for letting me know, I'll try to find some time to look into this. sponsoring me will prioritize that ;)

mrousavy avatar Aug 06 '24 15:08 mrousavy

thanks for letting me know, I'll try to find some time to look into this. sponsoring me will prioritize that ;)

Done -- Even though this issue is relatively low priority, doesn't affect me that much just wanted to show some appreciation!

ChristopherGabba avatar Aug 07 '24 12:08 ChristopherGabba

Thanks so much Christopher! :) ๐Ÿ™ โค๏ธ

mrousavy avatar Aug 07 '24 12:08 mrousavy

Hm, I'm browsing the diff but I really can't see anything that might've broken the naturalSize getter, for me it's also always returning 0 now... Are you guys both sure that 4.3.2 worked fine?

mrousavy avatar Aug 07 '24 13:08 mrousavy

Could this be an Xcode bug? lol.. I don't see how naturalSize can suddenly be 0x0 https://github.com/mrousavy/react-native-vision-camera/blob/77e98178f84824a0b1c76785469413b64dc96046/package/ios/Core/Recording/Track.swift#L59-L61

mrousavy avatar Aug 07 '24 13:08 mrousavy

Hm, I'm browsing the diff but I really can't see anything that might've broken the naturalSize getter, for me it's also always returning 0 now... Are you guys both sure that 4.3.2 worked fine?

Yeah, you're totally correct. I'm not sure what I was seeing yesterday, but is indeed broken on 4.3.2 too.

stewartiee avatar Aug 07 '24 14:08 stewartiee

I unfortunately bumped mine from 4.0.4 all the way to 4.5.1 in one big jump. I truthfully cannot confirm if the issue was there in 4.0.4 but I never remember seeing it until this new version. So I can't be of much help in the immediate until I revert versions and bump up until I find it.

ChristopherGabba avatar Aug 07 '24 16:08 ChristopherGabba

Okay I'll take another look at the changelog, maybe something was missing in the video configuration options

mrousavy avatar Aug 08 '24 10:08 mrousavy

Hey @ChristopherGabba sorry this is going to take a bit longer than usually because I'm in the middle of finishing Nitro Modules, and then I'm going to leave for a 5 day vacation. It's on my TODO list, promised!

mrousavy avatar Aug 20 '24 09:08 mrousavy

@mrousavy nitro on brother, no worries! Enjoy your vacation

ChristopherGabba avatar Aug 20 '24 09:08 ChristopherGabba

Can confirm this is happening on Android as well (Pixel 5 emulator)

jcgoodru avatar Sep 11 '24 14:09 jcgoodru

On my Android emulator the issue only happens when changing facing while recording. Otherwise it works properly.

bitsmanent avatar Sep 26 '24 14:09 bitsmanent

@mrousavy Along the same lines as this issue where it's returning the incorrect recorded size, on my physical Google Pixel 5 (I know its old), I predefine the camera format like so:

  const format = useCameraFormat(device, [
    { videoStabilizationMode: "standard" },
    { fps: targetFps },
    { videoAspectRatio: 16 / 9 },
    { videoResolution: { width: 308, height: 548 } }, // Note pre-defined size here
    { photoAspectRatio: 16 / 9 },
    { photoResolution: { width: 308, height: 548 } },
  ])

And after I finish recording a video, I get:

Finished recording with {
    "height": 1280,
    "width": 720,
    "duration": 5.579,
    "path": "/data/user/0/com.pwwindustries.reelfeel/cache/mrousavy-6443249496622499435.mp4"
}

Maybe while you are diagnosing out the 0, 0 issue on iOS you can quickly figure out why Android is returning a larger size than I describe up front in the initial config? Perhaps this is something simple that I'm overlooking though.

I know the comments on the videoResolution input read: The target resolution of the video (and frame processor) output pipeline. If no format supports the given resolution, the format closest to this value will be used. so maybe this is just the closest resolution possible?

ChristopherGabba avatar Sep 27 '24 15:09 ChristopherGabba

I think this is unrelated - Android might simply not have a format that small for video capture and chooses a different one.

But I'll figure out the iOS size issue either way! Lots of other priorities with Nitro atm, but I'll have some time soon

mrousavy avatar Sep 30 '24 18:09 mrousavy

@mrousavy 10-4!

ChristopherGabba avatar Sep 30 '24 19:09 ChristopherGabba

any updates?

vadimsetsko avatar Oct 22 '24 14:10 vadimsetsko

I'm also having this issue. version 4.5.3

iPhone XR iOS 17.6

It works fine on Android

phazei avatar Nov 21 '24 22:11 phazei

Is there an update on this?

It seems I am running into this issue when the camera is switched while recording. For example, if I start recording using the camera, and while recording I switch to the front camera, dimensions are now 0.

EDIT: I am on android

haquex19 avatar Feb 22 '25 00:02 haquex19

any updates? i still have this issue on iOS for 4.6.4

coinlordd avatar May 25 '25 08:05 coinlordd