TheWidlarzGroup / react-native-video

A <Video /> component for react-native
https://docs.thewidlarzgroup.com/react-native-video/
MIT License
7.2k stars 2.9k forks source link

onLoad never gets called, Video Player Stops working completely, App needs to be restarted- Android #2263

Closed AndresTIY closed 3 years ago

AndresTIY commented 3 years ago

Bug

This is a hard one to track and reproduce. onError doesn't get called when this happens.

I have a flatlist with several items. When the user taps on an item, a video loads in the top section and plays. The video being loaded at the moment is an mp4 coming from an AWS CDN link.

This works as expected on iOS. On Android, sometimes the video will either never load or stop playing during playback. I noticed that the onBuffer method is called with isBuffering: true and never switches back to false. When this issue happens when loading, the onLoadStart method is called but onLoad is never called. My activity indicator keeps on spinning and the video container has a black screen. If I try to load another video, I get the same issue. The url is stored in the reducer during the session, so if I clear the reducer and load it again, the issue still persists. The only way to fix this is by completely closing the app is opening it back up.

I've tried using the Android Media Player and still encounter the issue there.

Since this doesn't throw an error, I have no error message to produce.

Has anybody encountered this problem or know of a fix I could attempt?

Platform

Android Exoplayer Android Media Player

Environment info

React native info output:

System:
    OS: macOS 11.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 1.62 GB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 13.11.0 - /usr/local/bin/node
    Yarn: 1.22.4 - /usr/local/bin/yarn
    npm: 6.13.7 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  Managers:
    CocoaPods: 1.10.0 - /usr/local/bin/pod
  SDKs:
    iOS SDK:
      Platforms: iOS 14.2, DriverKit 20.0, macOS 11.0, tvOS 14.2, watchOS 7.1
    Android SDK:
      API Levels: 28, 29
      Build Tools: 28.0.3, 29.0.2, 30.0.0
      System Images: android-29 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom
      Android NDK: Not Found
  IDEs:
    Android Studio: 3.6 AI-192.7142.36.36.6241897
    Xcode: 12.2/12B45b - /usr/bin/xcodebuild
  Languages:
    Java: 1.8.0_222 - /usr/bin/javac
    Python: 2.7.16 - /usr/bin/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.13.1 => 16.13.1 
    react-native: 0.63.2 => 0.63.2 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Library version: 5.0.2

Steps To Reproduce

  1. Click on List Item
  2. Video should load
  3. 1 out of 10 times it does not load. When it doesn't, onLoad is not called and buffering is set to true
  4. Restart App
  5. Try again, Video loads this time

...

Expected behaviour

  1. Video should load. 2.There should be an error if the video does not load

Reproducible sample code

const Example = ({ mp4Url }) => {
  const [isBufferingNow, setIsBuffering] = useState(true);
  const [showControls, toggleShowControls] = useState(false);

  const videoRef = createRef<Video>();

  const onLoad = (data) => {
    console.log({ data });
  };

  const onBuffer = ({ isBuffering }) => {
    setIsBuffering(isBuffering);
  };

  return (
    <TouchableWithoutFeedback onPress={() => toggleShowControls(!showControls)}>
      <View>
        {isBufferingNow && (
          <View style={styles.videoLoadingIndicator}>
            <ActivityIndicator size="large" animating={isBufferingNow} />
          </View>
        )}
        {!!mp4Url && (
          <Video
            ref={videoRef}
            playWhenInactive
            playInBackground
            // paused={paused}
            controls={false}
            resizeMode="contain"
            muted={false}
            onLoad={onLoad}
            source={{ uri: mp4Url }}
            onBuffer={onBuffer}
            ignoreSilentSwitch="ignore"
          />
        )}
        {showControls && !!mp4Url && <PlayerControls />}
      </View>
    </TouchableWithoutFeedback>
  );
};

Video sample

If possible, include a link to the video that has the problem that can be streamed or downloaded from.

AndresTIY commented 3 years ago

I reviewed some other similar issues and tried a few things. Now, when I connect my device (Pixel 3aXL) directly and run it, the video will either buffer indefinitely before hitting onLoad or reach some point and then start buffering and stop the video indefinitely which is the same behavior as before, but if I tap on another video, it now loads up. If I let it indefinitely load, I eventually get a bug reported which looks like.

{
  "errorString": "Unrecognized media format",
  "errorException": "Unable to connect to https://hostLibrary/awsSomething/WhyWontThisPlay.mp4"
}

I created a function that reloads the video and it works! I pushed it up to our Google Play Alpha Channel and now I get the same exact behavior as before. isBuffering is true and never reverts to false on any video, requiring the app to be restarted to have videos function momentarily again.

Here's what I changed since the last post:

I upgrade to version 5.1.1

added this to my react native config file:

module.exports = {
  dependencies: {
    'react-native-video': {
      platforms: {
        android: {
          sourceDir: '../node_modules/react-native-video/android-exoplayer',
        },
      },
    },
  },
  project: {
    ios: {},
    android: {},
  },
  assets: ['./src/assets/fonts'],
};

then here's my settings.gradle

rootProject.name = 'AppName'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
include ':app'

build.gradle

dependencies {

    implementation "com.facebook.react:react-native:+"  // From node_modules
    compile project(':react-native-video')
    implementation "androidx.appcompat:appcompat:1.0.0"

    if (enableHermes) {
        def hermesPath = "../../node_modules/hermes-engine/android/";
        debugImplementation files(hermesPath + "hermes-debug.aar")
        releaseImplementation files(hermesPath + "hermes-release.aar")
    } else {
        implementation jscFlavor
    }
}

mainapplication.java

import com.brentvatne.react.ReactVideoPackage;
@Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // Packages that cannot be autolinked yet can be added manually here, for example:
          // packages.add(new MyReactNativePackage());
          new ReactVideoPackage();
          return packages;
        }

read elsewhere to make sure this is in the AndroidManifest.xml

<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />

path for this is src/debug/AndroidManifest.xml but I also tossed it in src/main/AndroidManifest.xml because I'm running out of ideas.

AndresTIY commented 3 years ago

source={{uri: 'https://www.vid.com/vid.mp4', type: 'mp4'}} seems to fix my issue so far!

lsnow99 commented 3 years ago

Hi, to clarify- all you had to do was add the type: 'mp4' to the input to source ? I just tried that and it isn't working for me :/

AndresTIY commented 3 years ago

So I think it was a combination of things that maaaaybe fixed it. The error happens less frequently but I just noticed it happening in production šŸ˜­

https://github.com/react-native-video/react-native-video#explicit-mimetype-for-the-stream

Using type was one of the things that helped since my videos are streaming.

Using 2 conditionals to render the video component also maybe helped. I have a videoUrl object containing 3 strings for mp4, m8u3, and a default url. This is being passed in from a parent component. Once that comes in, I set the mp4 url to the local state including a type

const [videoItem, setVideoItem] = useState({ url: '', type: '' });

useEffect(() => {
    setVideoItem({ url: videoUrls.mp4, type: 'mp4' });
  }, [videoUrls.mp4]);

return (
 {!!videoUrls.mp4 && !!videoItem.url && (
          <Video
            ref={videoRef}
            playWhenInactive
            playInBackground
            paused={paused}
            controls={false}
            resizeMode="contain"
            style={!fullscreen ? styles.videoPlayer : styles.fullScreenAndroid}
            muted={false}
            onLoad={onLoad}
            source={{
              uri: videoItem.url,
              type: videoItem.type,
            }}
            onBuffer={onBuffer}
            ignoreSilentSwitch="ignore"
            selectedTextTrack={selectedTextTrack}
            onProgress={onProgress}
            onEnd={onEnd}
            rate={rate}
            onError={handleError}
          />
        )}
)

So I console logged both !!videoUrls.mp4 and !!videoItem.url and this is the order I see it in:

 useEffect(() => {
    console.log({
      videoUrlTruthy: !!videoUrls.mp4,
      videoLocalStateTruthy: !!videoItem.url,
    });
  }, [videoItem.url, videoUrls.mp4]);

// {"videoLocalStateTruthy": false, "videoUrlTruthy": false}
// {"videoLocalStateTruthy": false, "videoUrlTruthy": true}
// {"videoLocalStateTruthy": true, "videoUrlTruthy": true}

and the log is consistent every time I tap on another video.

IF I remove one of those conditions, then I'm able to recreate the original error.

It's an odd one and I really don't understand why it's working now. Just gotta leave that code alone and hopefully it doesn't break down the road.

It'd be super interesting if this happens to work for you though!

Another thing: our videos originally started with https:// . I replaced it with http:// and it worked perfectly (with all the changes listed above) , but only in a local environment. In production, the error still occurred. So I went back and used the original https:// and now that works in production!

Hope this kinda helps :)

GustavPS commented 2 years ago

So I think it was a combination of things that maaaaybe fixed it. The error happens less frequently but I just noticed it happening in production šŸ˜­

https://github.com/react-native-video/react-native-video#explicit-mimetype-for-the-stream

Using type was one of the things that helped since my videos are streaming.

Using 2 conditionals to render the video component also maybe helped. I have a videoUrl object containing 3 strings for mp4, m8u3, and a default url. This is being passed in from a parent component. Once that comes in, I set the mp4 url to the local state including a type

const [videoItem, setVideoItem] = useState({ url: '', type: '' });

useEffect(() => {
    setVideoItem({ url: videoUrls.mp4, type: 'mp4' });
  }, [videoUrls.mp4]);

return (
 {!!videoUrls.mp4 && !!videoItem.url && (
          <Video
            ref={videoRef}
            playWhenInactive
            playInBackground
            paused={paused}
            controls={false}
            resizeMode="contain"
            style={!fullscreen ? styles.videoPlayer : styles.fullScreenAndroid}
            muted={false}
            onLoad={onLoad}
            source={{
              uri: videoItem.url,
              type: videoItem.type,
            }}
            onBuffer={onBuffer}
            ignoreSilentSwitch="ignore"
            selectedTextTrack={selectedTextTrack}
            onProgress={onProgress}
            onEnd={onEnd}
            rate={rate}
            onError={handleError}
          />
        )}
)

So I console logged both !!videoUrls.mp4 and !!videoItem.url and this is the order I see it in:

 useEffect(() => {
    console.log({
      videoUrlTruthy: !!videoUrls.mp4,
      videoLocalStateTruthy: !!videoItem.url,
    });
  }, [videoItem.url, videoUrls.mp4]);

// {"videoLocalStateTruthy": false, "videoUrlTruthy": false}
// {"videoLocalStateTruthy": false, "videoUrlTruthy": true}
// {"videoLocalStateTruthy": true, "videoUrlTruthy": true}

and the log is consistent every time I tap on another video.

IF I remove one of those conditions, then I'm able to recreate the original error.

It's an odd one and I really don't understand why it's working now. Just gotta leave that code alone and hopefully it doesn't break down the road.

It'd be super interesting if this happens to work for you though!

Another thing: our videos originally started with https:// . I replaced it with http:// and it worked perfectly (with all the changes listed above) , but only in a local environment. In production, the error still occurred. So I went back and used the original https:// and now that works in production!

Hope this kinda helps :)

I also have this problem, sadly none of this worked :/

brunolcarlos commented 2 years ago

I have the same problem https://github.com/expo/expo/issues/16403

IamMasterWayne commented 2 years ago

+1

vivaan-m commented 2 years ago

+1

freeboub commented 2 years ago

Not sure this is still reproduced on V6, let me know if it is the case

justintoth commented 1 year ago

This is still reproducible in v6, alpha 5. Only on Android, the onLoad event is not fired.

Yuniac commented 1 year ago

Just happened to me on IOS

ernestasgobionis commented 1 year ago

Reproducing this issue on Android, using 5.2.1. After a few successful loads, it just fires onLoadStart, but onLoad comes after a minute or two of leaving it idle

Update: Noticed that using default player instead of ExoPlayer, this issue does not reappear

singhagam1 commented 10 months ago

@ernestasgobionis how do you change this setting of using default player ? I am still stuck with this on iOS.

ernestasgobionis commented 10 months ago

@singhagam1 the default player setting is just for Android, I don't think there are different players on iOS

singhagam1 commented 10 months ago

@Yuniac Did you or anyone else find a solution for this in iOS ?

nicolaosm commented 10 months ago

What version are you testing on @ernestasgobionis @singhagam1 ?

singhagam1 commented 10 months ago

I am using 5.2.1 have tried with 6.0.0 too.

ernestasgobionis commented 10 months ago

I'm not working on that project anymore, but I was using 5.2.1. If i remember correctly I didn't have any issues on iOS. In the end I remember solving it using ExoPlayer and changing source URLs to http, that magically solved the loading problems. I was digging into the reason but now I can't remember anymore

nicolaosm commented 10 months ago

@singhagam1 have you tried beta3? is the issue still there?

singhagam1 commented 10 months ago

@nicolaosm
Just tried, same issue with 6.0.0-beta.3 Also want to let you now that I am using https urls served via CDN in a carousel. (Autoplay is off for video as well as carousel)

ckswopnera commented 9 months ago

for version 5.2.1 if I use default player then video sometimes was playing but black screen only the audio was coming. but I use exoplayer then onload() never get called. I also tried to do manual linking but no progress.

krunalinfynno commented 7 months ago

I am facing this issue as well on android. any solutions? react-native-video verison 6.0.0-alpha.11

edenmoyal1 commented 3 months ago

videoItem

So I think it was a combination of things that maaaaybe fixed it. The error happens less frequently but I just noticed it happening in production šŸ˜­

https://github.com/react-native-video/react-native-video#explicit-mimetype-for-the-stream

Using type was one of the things that helped since my videos are streaming.

Using 2 conditionals to render the video component also maybe helped. I have a videoUrl object containing 3 strings for mp4, m8u3, and a default url. This is being passed in from a parent component. Once that comes in, I set the mp4 url to the local state including a type

const [videoItem, setVideoItem] = useState({ url: '', type: '' });

useEffect(() => {
    setVideoItem({ url: videoUrls.mp4, type: 'mp4' });
  }, [videoUrls.mp4]);

return (
 {!!videoUrls.mp4 && !!videoItem.url && (
          <Video
            ref={videoRef}
            playWhenInactive
            playInBackground
            paused={paused}
            controls={false}
            resizeMode="contain"
            style={!fullscreen ? styles.videoPlayer : styles.fullScreenAndroid}
            muted={false}
            onLoad={onLoad}
            source={{
              uri: videoItem.url,
              type: videoItem.type,
            }}
            onBuffer={onBuffer}
            ignoreSilentSwitch="ignore"
            selectedTextTrack={selectedTextTrack}
            onProgress={onProgress}
            onEnd={onEnd}
            rate={rate}
            onError={handleError}
          />
        )}
)

So I console logged both !!videoUrls.mp4 and !!videoItem.url and this is the order I see it in:

 useEffect(() => {
    console.log({
      videoUrlTruthy: !!videoUrls.mp4,
      videoLocalStateTruthy: !!videoItem.url,
    });
  }, [videoItem.url, videoUrls.mp4]);

// {"videoLocalStateTruthy": false, "videoUrlTruthy": false}
// {"videoLocalStateTruthy": false, "videoUrlTruthy": true}
// {"videoLocalStateTruthy": true, "videoUrlTruthy": true}

and the log is consistent every time I tap on another video.

IF I remove one of those conditions, then I'm able to recreate the original error.

It's an odd one and I really don't understand why it's working now. Just gotta leave that code alone and hopefully it doesn't break down the road.

It'd be super interesting if this happens to work for you though!

Another thing: our videos originally started with https:// . I replaced it with http:// and it worked perfectly (with all the changes listed above) , but only in a local environment. In production, the error still occurred. So I went back and used the original https:// and now that works in production!

Hope this kinda helps :)

I noticed that when disabling onBuffer it also works, but because I need it, I tried replacing https to http and it worked, but only for debug, the issue remains for release.

regalstreak commented 1 month ago

If the url is https, check if emulator time is correct.

adb shell su root date $(date +%m%d%H%M%Y.%S)