diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f0646fa..4b2a4246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ ## Changelog -- Fix Android AudioFocus bug that could cause player to not respond to play/pause in some instances [#2311](https://github.com/react-native-video/react-native-video/pull/2311) +### Version 5.2.0 + +- Fix for tvOS native audio menu language selector +- Update ExoPlayer to allow pre-init and content clear [#2412] (https://github.com/react-native-video/react-native-video/pull/2412) +- iOS rate is reset to 1.0 after play/pause [#2167] (https://github.com/react-native-video/react-native-video/pull/2167) +- Upgrade ExoPlayer to 2.13.2 [#2317] (https://github.com/react-native-video/react-native-video/pull/2317) +- Fix AudioFocus pausing video when attempting to play [#2311] (https://github.com/react-native-video/react-native-video/pull/2311) ### Version 5.1.0-alpha9 diff --git a/README.md b/README.md index 0d30126b..9c396a2f 100644 --- a/README.md +++ b/README.md @@ -770,6 +770,12 @@ Platforms: Android ExoPlayer #### source Sets the media source. You can pass an asset loaded via require or an object with a uri. +Setting the source will trigger the player to attempt to load the provided media with all other given props. Please be sure that all props are provided before/at the same time as setting the source. + +Rendering the player component with a null source will init the player, and start playing once a source value is provided. + +Providing a null source value after loading a previous source will stop playback, and clear out the previous source content. + The docs for this prop are incomplete and will be updated as each option is investigated and tested. diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index f2469032..edd3c45b 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -514,6 +514,7 @@ class ReactExoplayerView extends FrameLayout implements player.prepare(mediaSource, !haveResumePosition, false); playerNeedsSource = false; + reLayout(exoPlayerView); eventEmitter.loadStart(); loadVideoStarted = true; } @@ -1058,7 +1059,6 @@ class ReactExoplayerView extends FrameLayout implements public void setSrc(final Uri uri, final String extension, Map headers) { if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; boolean isSourceEqual = uri.equals(srcUri); this.srcUri = uri; @@ -1068,12 +1068,23 @@ class ReactExoplayerView extends FrameLayout implements DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter, this.requestHeaders); - if (!isOriginalSourceNull && !isSourceEqual) { + if (!isSourceEqual) { reloadSource(); } } } + public void clearSrc() { + if (srcUri != null) { + player.stop(true); + this.srcUri = null; + this.extension = null; + this.requestHeaders = null; + this.mediaDataSourceFactory = null; + clearResumePosition(); + } + } + public void setProgressUpdateInterval(final float progressUpdateInterval) { mProgressUpdateInterval = progressUpdateInterval; } @@ -1084,14 +1095,13 @@ class ReactExoplayerView extends FrameLayout implements public void setRawSrc(final Uri uri, final String extension) { if (uri != null) { - boolean isOriginalSourceNull = srcUri == null; boolean isSourceEqual = uri.equals(srcUri); this.srcUri = uri; this.extension = extension; this.mediaDataSourceFactory = buildDataSourceFactory(true); - if (!isOriginalSourceNull && !isSourceEqual) { + if (!isSourceEqual) { reloadSource(); } } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 7980e66e..02ec4d48 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -145,6 +145,7 @@ public class ReactExoplayerViewManager extends ViewGroupManager headers = src.hasKey(PROP_SRC_HEADERS) ? toStringMap(src.getMap(PROP_SRC_HEADERS)) : null; if (TextUtils.isEmpty(uriString)) { + videoView.clearSrc(); return; } diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index 8394f6b0..a757c08a 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -918,6 +918,7 @@ static int const RCTVideoUnset = -1; - (void)setIgnoreSilentSwitch:(NSString *)ignoreSilentSwitch { _ignoreSilentSwitch = ignoreSilentSwitch; + [self configureAudio]; [self applyModifiers]; } @@ -933,29 +934,8 @@ static int const RCTVideoUnset = -1; [_player pause]; [_player setRate:0.0]; } else { - AVAudioSession *session = [AVAudioSession sharedInstance]; - AVAudioSessionCategory category = nil; - AVAudioSessionCategoryOptions options = nil; - if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { - category = AVAudioSessionCategoryPlayback; - } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { - category = AVAudioSessionCategoryAmbient; - } - - if([_mixWithOthers isEqualToString:@"mix"]) { - options = AVAudioSessionCategoryOptionMixWithOthers; - } else if([_mixWithOthers isEqualToString:@"duck"]) { - options = AVAudioSessionCategoryOptionDuckOthers; - } - - if (category != nil && options != nil) { - [session setCategory:category withOptions:options error:nil]; - } else if (category != nil && options == nil) { - [session setCategory:category error:nil]; - } else if (category == nil && options != nil) { - [session setCategory:session.category withOptions:options error:nil]; - } + [self configureAudio]; if (@available(iOS 10.0, *) && !_automaticallyWaitsToMinimizeStalling) { [_player playImmediatelyAtRate:_rate]; @@ -1089,6 +1069,33 @@ static int const RCTVideoUnset = -1; [self setAllowsExternalPlayback:_allowsExternalPlayback]; } +- (void)configureAudio +{ + AVAudioSession *session = [AVAudioSession sharedInstance]; + AVAudioSessionCategory category = nil; + AVAudioSessionCategoryOptions options = nil; + + if([_ignoreSilentSwitch isEqualToString:@"ignore"]) { + category = AVAudioSessionCategoryPlayback; + } else if([_ignoreSilentSwitch isEqualToString:@"obey"]) { + category = AVAudioSessionCategoryAmbient; + } + + if([_mixWithOthers isEqualToString:@"mix"]) { + options = AVAudioSessionCategoryOptionMixWithOthers; + } else if([_mixWithOthers isEqualToString:@"duck"]) { + options = AVAudioSessionCategoryOptionDuckOthers; + } + + if (category != nil && options != nil) { + [session setCategory:category withOptions:options error:nil]; + } else if (category != nil && options == nil) { + [session setCategory:category error:nil]; + } else if (category == nil && options != nil) { + [session setCategory:session.category withOptions:options error:nil]; + } +} + - (void)setRepeat:(BOOL)repeat { _repeat = repeat; } @@ -1130,12 +1137,20 @@ static int const RCTVideoUnset = -1; } } } else { // default. invalid type or "system" - [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; - return; + #if TARGET_OS_TV + // Do noting. Fix for tvOS native audio menu language selector + #else + [_player.currentItem selectMediaOptionAutomaticallyInMediaSelectionGroup:group]; + return; + #endif } - - // If a match isn't found, option will be nil and text tracks will be disabled - [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; + + #if TARGET_OS_TV + // Do noting. Fix for tvOS native audio menu language selector + #else + // If a match isn't found, option will be nil and text tracks will be disabled + [_player.currentItem selectMediaOption:mediaOption inMediaSelectionGroup:group]; + #endif } - (void)setSelectedAudioTrack:(NSDictionary *)selectedAudioTrack { diff --git a/package.json b/package.json index 136e59eb..cb8d146e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "5.1.1", + "version": "5.2.0", "description": "A