0

Current behavior / Bug

As soon as I pause/play the video on custom video control or slide on slider the video keep stutter to 1 or 2 seconds later sometim even 3-5 seconds

Video Clip Bug

https://drive.google.com/file/d/1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL/view?usp=sharing

Reproduction steps

  1. click button to enter the new screen
  2. video autoplay as soon the new screen load (if not doobleTap or singleTap on overlay, it will play as normal)
  3. singleTap on video's overlay for the custom controls to appear then click pause button to pause video.
  4. click play button to play video again, then video stutter.
  5. Sometime slide on slider to seek new video's currentTime, video stutter (rarely not stutter)
  6. If you pause the video the slide and play again and custom video control fadeout, video will play as normal not stutter

Expected behavior

  1. pause / play video not stutter.
  2. slide on progress video and play video again not stutter

Platform

Which player are you experiencing the problem on:

  • Android

React Native Setup

  • react-native : 0.64.2
  • gradle: 6.9
  • JDK: 11
  • Android Studio: Dolphin 2021.3.1
  • Android SDK API: 30
  • build tools: 30.0.2
  • @react-native-community/slider: 4.3.1
  • react-native-video: 5.2.0
  • react-native-orientation-locker: 1.5.0
  • react-native-vector-icons: 8.1.0

Remote Video URL

I called video URL from google cloud storage by using redux-saga. I already called dispatch function to called api from parent screen. So, in this screen I only useSelector to get URL from redux-saga. Can use any video URL with .mp4 file to substitute it.

Video sample

https://drive.google.com/uc?export=download&id=1C9Nj7FMcgA-I-I5Zha5jdJ-IBGXYGWJL

Sample Code

import React, { useRef, useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { View, StyleSheet, BackHandler, Dimensions, TouchableNativeFeedback, Text, StatusBar, Platform } from 'react-native';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation-locker';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Slider from '@react-native-community/slider';
import { normalize } from 'react-native-elements';

const { width, height } = Dimensions.get("window");
Icon.loadFont();
let overlayTimer;
let Timer;
const VideoPlayerScreen = (props) => {
    let lastTap = null;
    const dispatch = useDispatch();
    const { navigation } = props;
    const [Fullscreen, setFullscreen] = useState(false);
    const [paused, setpaused] = useState(false);
    const [currentTime, setcurrentTime] = useState(0);
    const [duration, setduration] = useState(0.1);
    const [overlay, setoverlay] = useState(false);
    const playerRef = useRef();
    const contract = useSelector((state) => state.contract.contract);

    useEffect(() => {
        const backHandler = BackHandler.addEventListener(
            "hardwareBackPress",
            backAction
        );
        return () => backHandler.remove();
    }, [])

    const backAction = () => {
        // navigation.goBack();
        return true;
    }

    const videoUri = contract.videoURL

    const FullscreenToggle = () => {

        if (Fullscreen) {
            Orientation.lockToPortrait();
            StatusBar.setHidden(false)
            navigation.setOptions({ headerShown: true });
            setFullscreen(false)
        } else {
            Orientation.lockToLandscape();
            StatusBar.setHidden(true)
            navigation.setOptions({ headerShown: false });
            setFullscreen(true);
        }

    }

    const handleDoubleTap = (doubleTapCallback, singleTapCallback) => {
        const now = Date.now();
        const DOUBLE_PRESS_DELAY = 300;
        if (lastTap && (now - lastTap) < DOUBLE_PRESS_DELAY) {
            clearTimeout(Timer);
            doubleTapCallback();
        } else {
            lastTap = now;
            Timer = setTimeout(() => {
                singleTapCallback();
            }, DOUBLE_PRESS_DELAY);
        }
    }

    const ShowHideOverlay = () => {
        handleDoubleTap(() => {
        }, () => {
            setoverlay(true)
            overlayTimer = setTimeout(() => setoverlay(false), 5000);
        })
    }
    const backward = () => {
        playerRef.current.seek(currentTime - 5);
        clearTimeout(overlayTimer);
        overlayTimer = setTimeout(() => setoverlay(false), 3000);
    }
    const forward = () => {
        playerRef.current.seek(currentTime + 5);
        clearTimeout(overlayTimer);
        overlayTimer = setTimeout(() => setoverlay(false), 3000);
    }
    const onslide = (slide) => {
        playerRef.current.seek(slide * duration);
        clearTimeout(overlayTimer);
        overlayTimer = setTimeout(() => setoverlay(false), 3000);
    }
    const getTime = (t) => {
        const digit = n => n < 10 ? `0${n}` : `${n}`;
        const sec = digit(Math.floor(t % 60));
        const min = digit(Math.floor((t / 60) % 60));
        const hr = digit(Math.floor((t / 3600) % 60));
        // return hr + ':' + min + ':' + sec; 
        return min + ':' + sec;
    }
    const load = ({ duration }) => setduration(duration);
    const progress = ({ currentTime }) => setcurrentTime(currentTime);
    return (
        <View style={styles.container}>
            {Platform.OS === 'android' ?
                < View style={Fullscreen ? styles.fullscreenVideo : styles.video}>
                    <Video
                        source={{ uri: videoUri }}
                        style={{ ...StyleSheet.absoluteFill }}
                        ref={playerRef}
                        paused={paused}
                        repeat={true}
                        onLoad={load}
                        onProgress={progress}
                        resizeMode={"contain"}
                        rate={1.0}
                    />
                    <View style={styles.overlay}>
                        {overlay ?
                            <View style={{ ...styles.overlaySet, backgroundColor: '#0006', alignItems: 'center', justifyContent: 'space-around' }}>
                                <View style={{ width: 50, height: 50 }}>
                                    <Icon name='replay-5' style={styles.icon} onPress={backward} />
                                </View>
                                <View style={{ width: 50, height: 50 }}>
                                    <Icon name={paused ? 'play-arrow' : 'pause'} style={styles.icon} onPress={() => setpaused(!paused)} />
                                </View>
                                <View style={{ width: 50, height: 50 }}>
                                    <Icon name='forward-5' style={styles.icon} onPress={forward} />
                                </View>
                                <View style={styles.sliderCont}>
                                    <View style={{ ...styles.timer, alignItems: 'center' }}>
                                        <View style={{ flexDirection: 'row' }}>
                                            <Text style={{ color: 'white' }}>{getTime(currentTime)}/</Text>
                                            <Text style={{ color: 'white' }}>{getTime(duration)}</Text>
                                        </View>
                                        <View style={{ margin: 5 }}>
                                            <Icon onPress={FullscreenToggle}
                                                name={Fullscreen ? 'fullscreen' : 'fullscreen-exit'}
                                                style={{ fontSize: 20, color: 'white' }} />
                                        </View>
                                    </View>
                                    <Slider
                                        style={{ margin: 5 }}
                                        maximumTrackTintColor='white'
                                        minimumTrackTintColor='white'
                                        thumbTintColor='white'
                                        value={currentTime / duration}
                                        onValueChange={onslide}
                                    />
                                </View>
                            </View>
                            :
                            <View style={styles.overlaySet}>
                                <TouchableNativeFeedback onPress={ShowHideOverlay}><View style={{ flex: 1 }} /></TouchableNativeFeedback>
                            </View>
                        }
                    </View>

                </View>
                :
                <View style={styles.video}>
                    <Video
                        source={{ uri: videoUri }}
                        style={{ width: width, aspectRatio: width / (height - normalize(110)) }}
                        controls
                    // ref={(ref) => {
                    //   this.player = ref;
                    // }}
                    />
                </View>
            }


        </View >
    )
}
const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: 'black'
    },
    // video: { width, height: width * .6, backgroundColor: 'black', justifyContent: 'center', alignItems: 'center' },
    video: { width: "100%", aspectRatio: width / (height - normalize(80)), backgroundColor: 'black', alignItems: 'center', justifyContent: 'center' },
    fullscreenVideo: {
        width: "100%",
        aspectRatio: 2 / 1,
        backgroundColor: 'black',
        ...StyleSheet.absoluteFill,
        elevation: 1
    },
    overlay: {
        ...StyleSheet.absoluteFillObject,
    },
    overlaySet: {
        flex: 1,
        flexDirection: 'row',
    },
    icon: {
        color: 'white',
        flex: 1,
        textAlign: 'center',
        textAlignVertical: 'center',
        fontSize: 25
    },
    TextStyle: {
        fontSize: 20, textAlign: 'center',
        marginVertical: 100, color: '#6200ee', fontWeight: 'bold'
    },
    sliderCont: {
        position: 'absolute',
        left: 0,
        right: 0,
        bottom: 0
    },
    timer: {
        width: '100%',
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingHorizontal: 5
    },
});
export default VideoPlayerScreen;
faijiuy
  • 55
  • 1
  • 9

0 Answers0