TheWidlarzGroup/react-native-video

When video starts in 'paused' state the onProgress event does not fire on Android

Closed this issue · 10 comments

Bug

onProgress callback does not fire on Android when video starts paused.

Platform

Which player are you experiencing the problem on:

  • Android ExoPlayer

Environment info

React native info output:

❯ ./node_modules/react-native/cli.js info
info Fetching system and libraries information...
System:
    OS: macOS 10.15.3
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 393.53 MB / 16.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 10.17.0 - /var/folders/4n/nd5y3s7x00gbr3mk4fq0jv4c0000gn/T/fnm-shell-2410913/bin/node
    Yarn: 1.22.4 - /usr/local/bin/yarn
    npm: 6.13.6 - /var/folders/4n/nd5y3s7x00gbr3mk4fq0jv4c0000gn/T/fnm-shell-2410913/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.9.1 - /Users/dylanjhaveri/.rvm/gems/ruby-2.5.1/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 13.4, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
    Android SDK: Not Found
  IDEs:
    Android Studio: 3.6 AI-192.7142.36.36.6241897
    Xcode: 11.4/11E146 - /usr/bin/xcodebuild
  Languages:
    Java: 12.0.2 - /usr/bin/javac
    Python: 2.7.17 - /usr/local/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.11.0 => 16.11.0
    react-native: 0.62.2 => 0.62.2
  npmGlobalPackages:
    *react-native*: Not Found

Library version: 5.0.2

Steps To Reproduce

Loom video recording: https://www.loom.com/share/0a903fc33c644ca9a7cb7c9747587997

  1. Start video in 'paused' state
  2. Play video
  3. onProgress callback does not fire on Android (it does fire on iOS)

Expected behaviour

  • When video starts out 'paused' the onProgress event should still fire

Reproducible sample code

See simple demo app here:

https://github.com/dylanjha/react-native-video-bugs


@cobarx I was surprised by this bug, any ideas if I'm doing something that is causing it. I thought maybe it was specific to my application but I was able to reproduce it in a sample app (github link above). Any help would be greatly appreciated 🙏

I'm trying to get an SDK out for Video QOS monitoring with his player (https://mux.com/data), but this is a blocker. I'm happy to work on a patch for it it, I started digging through the source code but I wasn't able to find an obvious fix (I'm also new to react-native world so there's a bit of a learning curve there)

@dylanjha : Just find out that it was working fine in my project but I have seen one thing in your repo that paused value is hardcoded in code which is set as true (if you don't provide value to attribute on the component then I will consider as true). But I think that based paused video player play and pause.

So you need to update the paused value with video current playing status in case if you are using custom controls over a player then you need to manage paused value and provide it to the player.

But in your repo, you are using default controls that come with react-native-video which internally handle state so you don't need to pass paused in the first place. But still, you want to manage video state from outside player control then you should provide proper value to the paused attribute. Means false when the video player is playing video and pauses when the video player is not playing a video.

I had clone your bug repo and added two button play and pause which update paused attribute. The default value of paused is set to true. But when you click on play custom button it changes to false and video start playing and you will start receiving progress event. here is your updates code.

import React, {useState} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
  Button,
} from 'react-native';

import {Colors} from 'react-native/Libraries/NewAppScreen';
import Video from 'react-native-video';

const App: () => React$Node = () => {
  const [showVideo, setShowVideo] = useState(false);
  const [isPause, setPauseStatus] = useState(true); // Added new code

  const onProgress = () => {
    // iOS - always gets logged
    // Android - if video starts in 'paused' state, this does not get logged [BUG]
    console.log('debug onProgress');
  };

  const onSeek = () => {
    // iOS - never gets logged
    // Android - gets logged [BUG]
    console.log('debug onSeek');
  };

  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          {global.HermesInternal == null ? null : (
            <View style={styles.engine}>
              <Text style={styles.footer}>Engine: Hermes</Text>
            </View>
          )}
          <View style={styles.body}>
            <Button
              title={showVideo ? 'Hide' : 'Show'}
              onPress={() => setShowVideo(!showVideo)}
            />
            {showVideo && (
              <View>
                <Video
                  style={styles.video}
                  source={{
                    uri:
                      'https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8',
                  }}
                  onSeek={onSeek}
                  onProgress={onProgress}
                  paused={isPause} // value updated with state variable 
                  controls
                  muted
                />
                <Button title="Play" onPress={() => setPauseStatus(false)} />
                <Button title="Pause" onPress={() => setPauseStatus(true)} />
              </View>
            )}
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: Colors.lighter,
  },
  video: {
    width: 400,
    height: 500,
  },
  engine: {
    position: 'absolute',
    right: 0,
  },
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.black,
  },
  footer: {
    color: Colors.dark,
    fontSize: 12,
    fontWeight: '600',
    padding: 4,
    paddingRight: 12,
    textAlign: 'right',
  },
});

export default App;

Interesting, thank you @jariwalabhavesh I can see what this code is doing.

To make sure I understand:

But in your repo, you are using default controls that come with react-native-video which internally handle state so you don't need to pass paused in the first place. But still, you want to manage video state from outside player control then you should provide proper value to the paused attribute. Means false when the video player is playing video and pauses when the video player is not playing a video.

This makes sense, but what is the recommended way if I want to use the default player controls, but start the player off in the paused state (in other words to not autoplay the video?).


For example, by default this video will autoplay:

<Video source={{uri: "background"}} />

If I want to not autoplay the video it seems like I should be able to do this:

<Video source={{uri: "background"}} paused />

However, based on this explanation it seems like I can only do that if I implement my own custom controls.


Furthermore, since this behavior in inconsistent on iOS vs. Andriod, so should that be considered a bug?

Am I missing something here?

@dylanjha : It looks like a bug if we consider using default player controls and want to keep the paused value as true hardcode.

But my opinion is that default controls are very basic. so the majority of users are using a third party control package. In my case, I had created custom control on a player from react-native.

But as of now, I think the workaround that I had provided in my last comment in your code should be enough to start working on that.

Maybe it will get fixed in future releases.

@jariwalabhavesh Thanks for confirming that you are seeing this bug, leaving this issue open with the hopes it gets some attention

Has this been solved yet?

@sjonchhe not to my knowledge

I would love to have an option to set paused to true or otherwise have an autoplay prop to disable a video from automatically playing.

I also noticed that the video may sometimes keep playing when you set paused = true right after you used the seek function. So looks like there's no reliable synchronization between the native player and the RN wrapper.

For me, onEnd event was causing this. It was calling while entering the screen.

This issue shall be fixed by #2664 on exoplayer progress is not reported while on pause