react-native-video: The app hangs for more than 10 seconds to start play on Android.

Problems

When click thumbnail to open the player page to play video, the app hangs for more than 10 seconds to show the player page.

Env

react-native: 0.39.2 react-native-video: Commits on Dec 13, 2016, The newest version even can not be compiled.

Code

The player page:

/**
 * 在球场
 * zaiqiuchang.com
 */

import React, {Component} from 'react';
import {StyleSheet, View, Text, CameraRoll, TouchableWithoutFeedback, Alert, Platform} from 'react-native';
import flattenStyle from 'flattenStyle';
import Video from 'react-native-video';
import Orientation from 'react-native-orientation';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';

import {COLOR, DEFAULT_NAV_BAR_STYLE, SCREEN_WIDTH, SCREEN_HEIGHT} from '../config';
import {navToPostDetail} from '../navigation';
import {VIDEO_RATES} from '../const';
import * as components from './';
import * as helpers from '../helpers';
import * as actions from '../actions';

class Player extends Component {
  static navigatorStyle = Object.assign({}, DEFAULT_NAV_BAR_STYLE, {
    navBarBackgroundColor: COLOR.backgroundDarkLighter,
    drawUnderNavBar: true,
    navBarTranslucent: true,
    navBarHideOnScroll: true,
    tabBarHidden: true,
  });

  constructor(props) {
    super(props);

    this.screenId = props.screenId || 'Player';

    let {navigator, cbRemove} = props;
    let rightButtons = [];
    if (cbRemove) {
      rightButtons.push({
        title: '删除',
        id: 'delete',
      });
    } else {
      rightButtons.push({
        title: '保存',
        id: 'save',
      });
    }
    navigator.setButtons({rightButtons});
    navigator.setOnNavigatorEvent(event => this.onNavigatorEvent(event));
  }

  componentDidMount() {
    let {navigator, network} = this.props;
    if (!network.isConnected || !network.reach) {
      Alert.alert(
        '播放失败',
        '网络未连接,或者类型未知。',
        [
          {text: '确认', onPress: () => navigator.pop()},
        ],
      );
      return;
    }

    this.orientationListener = orientation => {
      let {setPlayerState} = this.props;
      if (orientation == 'LANDSCAPE-LEFT' || orientation == 'LANDSCAPE-RIGHT') {
        setPlayerState({orientation});
      } else {
        setPlayerState({orientation: 'PORTRAIT'});
      }
    }
    Orientation.addSpecificOrientationListener(this.orientationListener);
    Orientation.unlockAllOrientations();

    let {autoPlay, account, setPlayerState} = this.props;
    if (autoPlay === undefined) {
      autoPlay = account.settings.video.autoPlay[network.reach];
    }
    setPlayerState({paused: !autoPlay});

    this.autoHideNavBar();
  }

  componentWillUnmount() {
    clearTimeout(this.navBarHiddenTimeout);

    let {resetPlayerState} = this.props;
    resetPlayerState();

    Orientation.removeOrientationListener(this.orientationListener);
    Orientation.lockToPortrait();
  }

  onNavigatorEvent(event) {
    let {navigator, videoUri, errorFlash, cbRemove} = this.props;
    if (event.type == 'NavBarButtonPress') {
      if (event.id == 'delete') {
        navigator.pop();
        cbRemove(videoUri);
      } else if (event.id == 'save') {
        CameraRoll.saveToCameraRoll(videoUri)
          .then(result => errorFlash('保存成功'))
          .catch(error => errorFlash('保存失败'));
      }
    }
  }

  autoHideNavBar(seconds=3000) {
    clearTimeout(this.navBarHiddenTimeout);
    this.navBarHiddenTimeout = setTimeout(() => {
      let {player, setPlayerState} = this.props;
      let {paused, rateSelectorVisible} = player;
      if (!paused && !rateSelectorVisible) {
        setPlayerState({navBarHidden: true});
      }
    }, seconds);
  }

  render() {
    let {navigator, network, object, videoUri, videoId, postId, prevScreen, 
      account, player, fileFavor, errorFlash, setPlayerState, favorFile, 
      unfavorFile} = this.props;

    if (!network.isConnected || !network.reach) {
      return null;
    }

    let {navBarHidden, orientation, loaded, paused, ended, rate, rateSelectorVisible, 
      naturalSize, currentTime, duration} = player;
    let videoFile = helpers.fileFromCache(object, videoId);
    let post;
    if (postId) {
      post = helpers.postFromCache(object, postId);
    }

    let maxRate = 'fhd';
    if (videoFile.pixelSize[0] < 1280) {
      maxRate = 'ld';
    } else if (videoFile.pixelSize[0] < 1920) {
      maxRate = 'hd';
    }
    rate = rate || account.settings.video.playRate[network.reach];
    if (maxRate == 'ld' && (rate == 'hd' || rate == 'fhd')) {
      rate = 'ld';
    } else if (maxRate == 'hd' && rate == 'fhd') {
      rate = 'hd';
    }

    let favoredFiles = fileFavor.favoredFiles || [];
    favored = favoredFiles.includes(videoId);

    navigator.toggleNavBar({'to': (navBarHidden ? 'hidden' : 'shown')});

    let videoWidth = orientation == 'PORTRAIT' ? SCREEN_WIDTH : SCREEN_HEIGHT;
    let videoHeight = Math.round(videoWidth * videoFile.pixelSize[1] / videoFile.pixelSize[0]);

    let {width: opBarWidth, height: opBarHeight} = flattenStyle(styles.opBar);
    let opBarLeft = Math.round(((orientation == 'PORTRAIT' ? SCREEN_WIDTH : SCREEN_HEIGHT) - opBarWidth) / 2);
    let opBarTop = Math.round(((orientation == 'PORTRAIT' ? SCREEN_HEIGHT : SCREEN_WIDTH) - opBarHeight) / 2);

    return (
      <components.Layout screenId={this.screenId}>
        <TouchableWithoutFeedback
          onPress={() => {
            setPlayerState({navBarHidden: !navBarHidden});
            this.autoHideNavBar();
          }}
        >
          <View style={{flex: 1, justifyContent: 'center', backgroundColor: 'black'}}>
            <Video 
              source={helpers.videoSource(videoUri, rate)}
              repeat={false}
              paused={paused}
              onLoadStart={event => {
                let {src} = event;
                setPlayerState({src});
              }}
              onLoad={event => {
                let {duration, naturalSize} = event;
                setPlayerState({duration, naturalSize, loaded: true});
                this.player.seek(0);
              }}
              onProgress={event => {
                let {currentTime, playableDuration} = event;
                setPlayerState({currentTime, playableDuration});
              }}
              onEnd={event => {
                Orientation.lockToPortrait();
                setPlayerState({
                  navBarHidden: false, 
                  paused: true, 
                  ended: true,
                });
              }}
              onError={error => errorFlash(error)}
              ref={ref => this.player = ref}
              style={[styles.video, {width: videoWidth, height: videoHeight}]}
            />

            {!navBarHidden ? 
            <View style={[styles.opBar, {top: opBarTop, left: opBarLeft}]}>
              {!ended ?
              <components.Icon 
                name={paused ? 'play-circle-outline' : 'pause-circle-outline'} 
                onPress={() => {
                  setPlayerState({paused: !paused});
                  this.autoHideNavBar();
                }}
                style={styles.opText}
              /> :
              null}

              {ended ?
              <View style={styles.opContainer}>
                <components.Icon 
                  name='replay' 
                  onPress={() => {
                    this.player.seek(0);
                    setPlayerState({ended: false, paused: false});
                    this.autoHideNavBar();
                  }}
                  style={styles.opText}
                />
                
                {videoId ?
                <components.Icon 
                  name={favored ? 'favorite' : 'favorite-border'} 
                  onPress={() => {
                    navigator.pop();
                    if (favored) {
                      unfavorFile({fileId: videoId, cbOk: () => errorFlash('取消收藏成功。')});
                    } else {
                      favorFile({fileId: videoId, cbOk: () => errorFlash('收藏成功。')});
                    }
                  }}
                  style={[styles.opText, (favored ? {color: COLOR.favored} : null)]}
                /> : 
                null}
                {post ?
                <components.Icon 
                  name='more-horiz' 
                  onPress={() => {
                    navigator.pop();
                    if (prevScreen != 'PostDetail') {
                      navToPostDetail(navigator, post);
                    }
                  }}
                  style={styles.opText}
                /> : 
                null}
              </View> : 
              null}
            </View> : 
            null}
            
            {!navBarHidden ? 
            <View style={styles.ctlBar}>
              <View style={{flexDirection: 'row', alignItems: 'center'}}>
                <components.Text style={styles.ctlBarText}>{helpers.durationText(currentTime)} / {helpers.durationText(duration)}</components.Text>
              </View>
              <View style={{flexDirection: 'row', alignItems: 'center'}}>
                <components.Text style={styles.ctlBarText}
                  onPress={() => {
                    setPlayerState({rateSelectorVisible: !rateSelectorVisible});
                    this.autoHideNavBar();
                  }}
                >
                {helpers.videoRateText(rate)}
                </components.Text>

                {Platform.OS == 'ios' ? 
                <components.Icon 
                  name={orientation == 'PORTRAIT' ? 'fullscreen' : 'fullscreen-exit'}
                  onPress={() => {
                    setPlayerState({paused: true});
                    if (orientation == 'PORTRAIT') {
                      setPlayerState({orientation: 'LANDSCAPE-LEFT'});
                      Orientation.lockToLandscapeLeft();
                    } else {
                      setPlayerState({orientation: 'PORTRAIT'});
                      Orientation.lockToPortrait();
                    }
                    setPlayerState({paused: false});
                  }}
                  style={[styles.ctlBarText, {padding: 5, fontSize: 22}]}
                /> : 
                null}
              </View>
            </View> :
            null}

            {!navBarHidden && rateSelectorVisible ?
            <View style={styles.rateSelector}>
              {VIDEO_RATES.filter(v => {
                if (v.value == rate) {
                  return false;
                }
                if (maxRate == 'ld' && (v.value == 'hd' || v.value == 'fhd')) {
                  return false;
                } else if (maxRate == 'hd' && v.value == 'fhd') {
                  return false;
                }
                return true;
              })
                .map(({label, value}) => 
                  <components.Text
                    key={value}
                    onPress={() => {
                      setPlayerState({
                        rate: value, 
                        rateSelectorVisible: false, 
                        loaded: false,
                        paused: false, 
                        ended: false,
                      });
                      this.autoHideNavBar();
                    }}
                    style={styles.ctlBarText}
                  >
                    {label}
                  </components.Text>
                )
            }
            </View> :
            null}
          </View>
        </TouchableWithoutFeedback>
      </components.Layout>
    );
  }
}

const styles = StyleSheet.create({
  video: {},
  opBar: {
    position: 'absolute',
    width: 300,
    height: 100,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  opContainer: {
    flexDirection: 'row',
    padding: 5,
    borderRadius: 10,
    backgroundColor: COLOR.backgroundDarkLighter + '80',
  },
  opText: {
    color: COLOR.textLightNormal,
    opacity: 0.8,
    backgroundColor: 'transparent',
    fontSize: 48,
    margin: 5,
  },
  ctlBar: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    backgroundColor: COLOR.backgroundDarkLighter + '80',
  },
  ctlBarText: {
    color: COLOR.textLightNormal,
    fontSize: 12,
    padding: 10,
  },
  rateSelector: {
    position: 'absolute',
    bottom: 32,
    right: (Platform.OS == 'ios' ? 32 : 0),
    alignItems: 'flex-end',
  },
});

function mapStateToProps(state) {
  let {network, object, account, player, fileFavor} = state;
  return {
    network,
    object,
    account,
    player,
    fileFavor,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(actions, dispatch);
}

export default connect(mapStateToProps, mapDispatchToProps)(Player);

Screenshot

It works great on iOS, and the ui is as following.

image

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 18 (3 by maintainers)

Most upvoted comments

Seems like ExoPlayer / prepareAsync fixes this issue.

@haroonob search through similar issues and you will find the answer. Let’s try and keep the discussion on this thread down, as these are NPM basics. Thank you.

same problem, need to rollback 0.9.0

Hi guys, I get the same problem. I’m using RN 0.41.2, RN Video 1.0.0, Whenever the video is loaded, It makes the app hang for 10 seconds. I downgrade RN Video to 0.9.0 then it’s not hang any more but got other problems: full screen is not working and when the app is switched to landscape mode, the video screen is not scaled, the width is expanded but the height is not expanded, so the video is displayed with a half of screen size. What should I do now 😦