itsnubix/react-native-video-controls

Render Error [2209, "RCTVIEW",{"width":"<<NaN>>"}] is not usable as a native method argument

jrhager84 opened this issue · 9 comments

When using the Video component from react-native-video-controls, I get this error. With the Video component from react-native-video, it works perfectly. I also have found that if I leave the controls enabled for the native controls, the error doesn't happen. Is there something I'm doing wrong?

My Base player:

import React, { ReactPropTypes, SetStateAction, useCallback, useState } from 'react';
import Video1, { VideoProperties } from 'react-native-video';
import Video from 'react-native-video-controls';
import { Animated, StyleSheet, Text, View } from 'react-native';
import { Image } from 'react-native-elements';
import Icon from 'react-native-vector-icons/FontAwesome';
import { colors } from '../styles/colorPalette';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { useTheme } from '../contexts/ThemeContext';
import { ReactNativeProps } from 'react-native-render-html';

interface VideoPlayerProps extends VideoProperties {
  autoPlay?: boolean
  categoryOverlay?: boolean | string
  ref?: any
}

const VideoPlayer = (props: VideoPlayerProps & ReactNativeProps) => {
  const [vidAspectRatio, setVidAspectRatio] = useState(1.8)
  const [duration, setDuration] = useState(null);
  const [progress, setProgress] = useState(1);
  const [isPlaying, setIsPlaying] = useState(!props.paused || false);
  const [controlsActive, setControlsActive] = useState(false);
  const { darkMode } = useTheme();
  const { categoryOverlay } = props;

  const handleHeight = (res: any) => {
    // set height and duration
    setDuration && setDuration(res?.duration);
    setVidAspectRatio(res?.naturalSize?.width / res?.naturalSize?.height || 1.8);
  }

  const handlePause = (res: any) => {
    // The logic to handle the pause/play logic
    res.playbackRate === 0 ? setIsPlaying(false) : setIsPlaying(true);
  }

  const handleProgress = (progress: any) => {
    setProgress(progress.atValue);
  }

  const styles = StyleSheet.create({
    container: {
      position: 'relative',
      resizeMode: 'cover',
      aspectRatio: vidAspectRatio,
      justifyContent: 'center',
      alignItems: 'center',
    },
    playIcon: {
      color: darkMode ? colors.primary.purple4 : "#fff",
      fontSize: 30,
    },
    playIconContainer: {
      backgroundColor: darkMode ? '#fff' : colors.primary.purple4,
      paddingHorizontal: 15,
      paddingVertical: 7.5,
      borderRadius: 10,
    },
    video: {
      flex: 1,
      position: 'absolute',
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
    },
    videoPoster: {
      position: 'absolute',
      top: 0,
      bottom: 0,
      left: 0,
      right: 0,
      resizeMode: 'cover',
    },
    categoryOverlay: {
      paddingHorizontal: 10,
      paddingVertical: 5,
      position: 'absolute',
      color: '#fff',
      bottom: 10,
      right: 10,
      backgroundColor: 'rgba(0,0,0, .75)',
      borderRadius: 10,
      zIndex: 999,
      textTransform: 'uppercase',
    },
  });

  const convertTime = (seconds: number) => {
    const secsRemaining = Math.floor(seconds % 60);
    return `${Math.floor(seconds / 60)}:${secsRemaining < 10 ? '0' + secsRemaining : secsRemaining}`
  }

  const convertTimeV2 = (secs: number) => {
    var hours   = Math.floor(secs / 3600)
    var minutes = Math.floor(secs / 60) % 60
    var seconds = Math.floor(secs % 60)

    return [hours,minutes,seconds]
        .map(v => v < 10 ? "0" + v : v)
        .filter((v,i) => v !== "00" || i > 0)
        .join(":")
  }

  return (
    <View style={styles.container}>
      <Video 
        {...props}
        /* controls */
        disableBack
        disableFullscreen
        disablePlayPause
        paused={!isPlaying}
        onLoad={handleHeight}
        onHideControls={() => setControlsActive(false)}
        onShowControls={() => setControlsActive(true)}
        onPlaybackRateChange={handlePause}
        onProgress={handleProgress}
        style={styles.video} 
        videoStyle={styles.video} 
        seekColor="#a146b7" 
        controlTimeout={3000} 
        tapAnywhereToPause
      />
      {categoryOverlay && progress === 1 && 
        <View style={styles.categoryOverlay}>
          <Text style={{color: "#fff", textTransform: 'uppercase'}}>{(typeof categoryOverlay === 'boolean') && duration ? convertTime(duration) : categoryOverlay}</Text>
        </View>
      }
      { (progress === 1 && !isPlaying) && <View style={styles.videoPoster}><Image style={{width: '100%', height: '100%', resizeMode: 'cover'}} source={{ uri: props.poster }} /></View> }
      { (!isPlaying || controlsActive) && 
        <Animated.View style={styles.playIconContainer}>
          <TouchableOpacity onPress={() => isPlaying ? setIsPlaying(false) : setIsPlaying(true)}>
            <Icon style={styles.playIcon} name={isPlaying ? "pause" : "play"} />
          </TouchableOpacity>
        </Animated.View> }
    </View>
  );
}

export default VideoPlayer

My live video player:

import React, { useCallback, useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import VideoPlayer from './VideoPlayer';
import Icon from 'react-native-vector-icons/FontAwesome';
import { colors } from '../styles/colorPalette';
import PeopleCard from '../components/PeopleCard';
import { useTheme } from '../contexts/ThemeContext'

export default function LiveVideoCard(props: any) {
  const { darkMode } = useTheme();
  const [link, setLink] = useState<LiveVideoResponse | null>(null)
  const [loading, setLoading] = useState(true)

  interface LiveVideoResponse {
    poster: string
    title: string
    url: string
  }

  const styles = StyleSheet.create({
    container: {
      backgroundColor: darkMode ? colors.background.darkBg : '#fff',
      flex: 1,
      padding: 20,
      borderTopWidth: 1,
      borderBottomWidth: 1,
      borderStyle: 'solid',
      borderColor: 'rgba(0,0,0,0.25)',
    },
    inner: {
      backgroundColor: darkMode ? colors.surface.dark1 : '#fff',
      borderWidth: 1,
      borderColor: darkMode ? 'rgba(255,255,255,0.25)' : 'rgba(0,0,0,0.25)',
      borderRadius: 20,
      paddingVertical: 20,
    },
    title: {
      fontWeight: 'bold',
      fontSize: 16,
      color: darkMode ? '#fff' : colors.primary.purple4,
      marginBottom: 10,
    },
    body: {
      color: darkMode ? '#efefef' : 'black'
    },
  })

  const getLink = useCallback(async () => {
      try {
        const data = await fetch('https://test.com/config_live_stream/get_live_stream', {
          method: 'GET',
          headers: {
            Accept: 'application/json',
            'Content-Type': 'application/json',
            'test': 'test'
          },
        })
      const json = await data.json()
      setLink(json.data[0])
      } catch(e) {
        console.error(e)
      }
      finally {
        setLoading(false)
      }
    }, [link])

  useEffect(() => {
    getLink();
  }, [])

  return (
    !loading && link ?
    <View style={styles.container}>
      <Text style={{color: darkMode ? '#fff' : 'black', fontFamily: "Sequel 100 Wide 65", fontSize: 13, marginBottom: 20, paddingHorizontal: 10}} ><Icon name="circle" size={10} style={{paddingLeft:5, color:"#e32525"}}  /> Live now</Text>
      <View style={styles.inner}>
        <PeopleCard data={{ image_url: link.poster, title: 'test', }} />
        <VideoPlayer paused={false} muted={true} source={{ uri: link.url }} />
        <View style={{padding: 20}}>
          <Text style={styles.title}>{link.title}</Text>
        </View>
      </View>
    </View>
    : null
  )
}

The error in Metro:

Error: Exception in HostFunction: Malformed calls from JS: field sizes are different.

[[73,50,50,3],[0,6,6,3],[[66,0,1639731698612,false],[1]],3174]

This error is located at:
    in PanGestureHandler (at GestureHandlerNative.tsx:14)
    in PanGestureHandler (at Drawer.tsx:532)
    in DrawerView (at DrawerView.tsx:273)
    in DrawerViewBase (at DrawerView.tsx:317)
    in RCTView (at View.js:32)
    in View (at DrawerView.tsx:316)
    in RCTView (at View.js:32)
    in View (at SafeAreaProviderCompat.tsx:42)
    in SafeAreaProviderCompat (at DrawerView.tsx:315)
    in DrawerView (at createDrawerNavigator.tsx:126)
    in Unknown (at createDrawerNavigator.tsx:125)
    in DrawerNavigator (at App.tsx:47)
    in EnsureSingleNavigator (at BaseNavigationContainer.tsx:430)
    in BaseNavigationContainer (at NavigationContainer.tsx:132)
    in ThemeProvider (at NavigationContainer.tsx:131)
    in NavigationContainerInner (at App.tsx:45)
    in RNCSafeAreaProvider (at SafeAreaContext.tsx:76)
    in SafeAreaProvider (at App.tsx:44)
    in CustomApp (at App.tsx:82)
    in ThemeContextProvider (at App.tsx:81)
    in App (at renderApplication.js:50)
    in RCTView (at View.js:32)
    in View (at AppContainer.js:92)
    in RCTView (at View.js:32)
    in View (at AppContainer.js:119)
    in AppContainer (at renderApplication.js:43)
    in testmobile(RootComponent) (at renderApplication.js:60), js engine: hermes

Same problem ,

What version are you using? I'm encountering similar issues with react-native@0.69.2, react-native-video@^6.0.0-alpha.1, react-native-video-controls@^2.8.1

ursnj commented

Same for me too.... Any solution ???

In my case the crash was due to using react-native-gesture-handler on a specific app page (the same where the video component was*). After removing usages of react-native-gesture-handler in that page and switching previous logic to React Native's gesture system, the page worked fine without crashing.

*I'm not using react-native-video-controls directly, but its dependency react-native-video (i needed my own custom video controls UI) but the issue is still relevant.

I fixed this problems with

import { Dimensions } from "react-native";
// ...

// render() component
<VideoPlayer style={{
    left: 0,
    top: 0,
    bottom: 0,
    right: 0,
    width: Dimensions.get('window').width,
    height: 2160 * (Dimensions.get('window').width / 3840)
}} source={{ uri: "https://www.google.com/google.mp4" }}
/>

// this will fit your player to 16:9 aspect ratio

I had to patch the package to replace
const percent = this.state.currentTime / this.state.duration;
with
const percent = this.state.duration === 0 ? 0 : this.state.currentTime / this.state.duration;
in VideoPlayer.js since it doesn't look like they're merging any PRs recently

Now i know this error only occurred in DEBUG mode.
I fixed it by briefly pressing the pause button, then waiting for the video to finish loading and then pressing the play button.

ursnj commented

I had to patch the package to replace const percent = this.state.currentTime / this.state.duration; with const percent = this.state.duration === 0 ? 0 : this.state.currentTime / this.state.duration; in VideoPlayer.js since it doesn't look like they're merging any PRs recently

This is working, can we raise and merge PR ???

I had to patch the package to replace
const percent = this.state.currentTime / this.state.duration;
with
const percent = this.state.duration === 0 ? 0 : this.state.currentTime / this.state.duration;
in VideoPlayer.js since it doesn't look like they're merging any PRs recently

This is working! Thanks!