From 522af9eaa7d1b4e013a5bd835c1439f7f92d0ee4 Mon Sep 17 00:00:00 2001 From: redspear Date: Mon, 28 Sep 2020 09:56:06 +1000 Subject: [PATCH 1/3] Bugfix: #1930 --- ios/Video/RCTVideo.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ios/Video/RCTVideo.m b/ios/Video/RCTVideo.m index 8780f48f..a26c1bdd 100644 --- a/ios/Video/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -718,7 +718,10 @@ static int const RCTVideoUnset = -1; } } else if (object == _player) { if([keyPath isEqualToString:playbackRate]) { - if(self.onPlaybackRateChange) { + if (_player.rate > 0 && _rate > 0 && _player.rate != _rate) { + // Playback is resuming, apply rate modifer. + [_player setRate:_rate]; + } else if(self.onPlaybackRateChange) { self.onPlaybackRateChange(@{@"playbackRate": [NSNumber numberWithFloat:_player.rate], @"target": self.reactTag}); } From fcc66df9452eb4a263da6dbcfec81e06d5a59088 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Thu, 24 Jun 2021 08:00:38 +0300 Subject: [PATCH 2/3] Fix AudoFocus pausing video when attempting to play (#2311) Fix AudioFocus bug that could cause the player to stop responding to play/pause in some instances. Fixes issue #1945 This was caused by the player requesting audio focus on each play (un-pause) and that resulted in a small window of Audio focus loss and then gain. The focus loss results in the player being paused while the player was supposed to play at the time. The solution is to keep track of Audio focus and not request new focus if we already have it. --- CHANGELOG.md | 2 ++ .../com/brentvatne/exoplayer/ReactExoplayerView.java | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b717fa0c..5f0646fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 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.1.0-alpha9 - Add ARM64 support for windows [#2137](https://github.com/react-native-community/react-native-video/pull/2137) 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 b41b2768..20ce983a 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -122,6 +122,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean isPaused; private boolean isBuffering; private boolean muted = false; + private boolean hasAudioFocus = false; private float rate = 1f; private float audioVolume = 1f; private int minLoadRetryCount = 3; @@ -567,7 +568,7 @@ class ReactExoplayerView extends FrameLayout implements } private boolean requestAudioFocus() { - if (disableFocus || srcUri == null) { + if (disableFocus || srcUri == null || this.hasAudioFocus) { return true; } int result = audioManager.requestAudioFocus(this, @@ -582,8 +583,8 @@ class ReactExoplayerView extends FrameLayout implements } if (playWhenReady) { - boolean hasAudioFocus = requestAudioFocus(); - if (hasAudioFocus) { + this.hasAudioFocus = requestAudioFocus(); + if (this.hasAudioFocus) { player.setPlayWhenReady(true); } } else { @@ -678,6 +679,7 @@ class ReactExoplayerView extends FrameLayout implements public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_LOSS: + this.hasAudioFocus = false; eventEmitter.audioFocusChanged(false); pausePlayback(); audioManager.abandonAudioFocus(this); @@ -686,6 +688,7 @@ class ReactExoplayerView extends FrameLayout implements eventEmitter.audioFocusChanged(false); break; case AudioManager.AUDIOFOCUS_GAIN: + this.hasAudioFocus = true; eventEmitter.audioFocusChanged(true); break; default: From 141192a56d4ecd6ff1e2ce5c5d587e047318ba19 Mon Sep 17 00:00:00 2001 From: Armands Malejev Date: Thu, 24 Jun 2021 08:01:11 +0300 Subject: [PATCH 3/3] Upgrade ExoPlayer to 2.13.2 (#2317) Upgrade ExoPlayer from 2.11.4 to 2.13.2 and fix any issues related to the upgrade and deprecated method use. --- android-exoplayer/build.gradle | 4 +- .../brentvatne/exoplayer/ExoPlayerView.java | 25 +++++-- .../exoplayer/ReactExoplayerView.java | 74 ++++++++++--------- 3 files changed, 60 insertions(+), 43 deletions(-) diff --git a/android-exoplayer/build.gradle b/android-exoplayer/build.gradle index 8ebe1d25..d005a58f 100644 --- a/android-exoplayer/build.gradle +++ b/android-exoplayer/build.gradle @@ -28,7 +28,7 @@ android { dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" - implementation('com.google.android.exoplayer:exoplayer:2.11.4') { + implementation('com.google.android.exoplayer:exoplayer:2.13.2') { exclude group: 'com.android.support' } @@ -37,7 +37,7 @@ dependencies { implementation "androidx.core:core:1.1.0" implementation "androidx.media:media:1.1.0" - implementation('com.google.android.exoplayer:extension-okhttp:2.11.4') { + implementation('com.google.android.exoplayer:extension-okhttp:2.13.2') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}' diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index afa5106c..61cb0dd6 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -17,6 +17,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.text.Cue; @@ -85,6 +86,14 @@ public final class ExoPlayerView extends FrameLayout { addViewInLayout(layout, 0, aspectRatioParams); } + private void clearVideoView() { + if (surfaceView instanceof TextureView) { + player.clearVideoTextureView((TextureView) surfaceView); + } else if (surfaceView instanceof SurfaceView) { + player.clearVideoSurfaceView((SurfaceView) surfaceView); + } + } + private void setVideoView() { if (surfaceView instanceof TextureView) { player.setVideoTextureView((TextureView) surfaceView); @@ -113,8 +122,8 @@ public final class ExoPlayerView extends FrameLayout { } /** - * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and - * {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous + * Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and + * {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous * assignments are overridden. * * @param player The {@link SimpleExoPlayer} to use. @@ -124,18 +133,18 @@ public final class ExoPlayerView extends FrameLayout { return; } if (this.player != null) { - this.player.setTextOutput(null); - this.player.setVideoListener(null); + this.player.removeTextOutput(componentListener); + this.player.removeVideoListener(componentListener); this.player.removeListener(componentListener); - this.player.setVideoSurface(null); + clearVideoView(); } this.player = player; shutterView.setVisibility(VISIBLE); if (player != null) { setVideoView(); - player.setVideoListener(componentListener); + player.addVideoListener(componentListener); player.addListener(componentListener); - player.setTextOutput(componentListener); + player.addTextOutput(componentListener); } } @@ -205,7 +214,7 @@ public final class ExoPlayerView extends FrameLayout { layout.invalidateAspectRatio(); } - private final class ComponentListener implements SimpleExoPlayer.VideoListener, + private final class ComponentListener implements VideoListener, TextOutput, ExoPlayer.EventListener { // TextRenderer.Output implementation 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 20ce983a..d7c33214 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -37,7 +37,7 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; @@ -62,7 +62,7 @@ import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; -import com.google.android.exoplayer2.trackselection.TrackSelection; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.upstream.BandwidthMeter; @@ -88,7 +88,7 @@ class ReactExoplayerView extends FrameLayout implements BecomingNoisyListener, AudioManager.OnAudioFocusChangeListener, MetadataOutput, - DefaultDrmSessionEventListener { + DrmSessionEventListener { private static final String TAG = "ReactExoplayerView"; @@ -395,7 +395,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void run() { if (player == null) { - TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); + ExoTrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); trackSelector.setParameters(trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); @@ -410,23 +410,11 @@ class ReactExoplayerView extends FrameLayout implements DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); - // DRM - DrmSessionManager drmSessionManager = null; - if (self.drmUUID != null) { - try { - drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, - self.drmLicenseHeader); - } catch (UnsupportedDrmException e) { - int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported - : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME - ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); - eventEmitter.error(getResources().getString(errorStringId), e); - return; - } - } - // End DRM - player = ExoPlayerFactory.newSimpleInstance(getContext(), renderersFactory, - trackSelector, defaultLoadControl, drmSessionManager, bandwidthMeter); + player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) + .setTrackSelector​(trackSelector) + .setBandwidthMeter(bandwidthMeter) + .setLoadControl(defaultLoadControl) + .build(); player.addListener(self); player.addMetadataOutput(self); exoPlayerView.setPlayer(player); @@ -441,8 +429,24 @@ class ReactExoplayerView extends FrameLayout implements if (playerNeedsSource && srcUri != null) { exoPlayerView.invalidateAspectRatio(); + // DRM + DrmSessionManager drmSessionManager = null; + if (self.drmUUID != null) { + try { + drmSessionManager = buildDrmSessionManager(self.drmUUID, self.drmLicenseUrl, + self.drmLicenseHeader); + } catch (UnsupportedDrmException e) { + int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported + : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME + ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); + eventEmitter.error(getResources().getString(errorStringId), e); + return; + } + } + // End DRM + ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension); + MediaSource videoSource = buildMediaSource(srcUri, extension, drmSessionManager); MediaSource mediaSource; if (mediaSourceList.size() == 0) { mediaSource = videoSource; @@ -473,7 +477,7 @@ class ReactExoplayerView extends FrameLayout implements }, 1); } - private DrmSessionManager buildDrmSessionManager(UUID uuid, + private DrmSessionManager buildDrmSessionManager(UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException { if (Util.SDK_INT < 18) { return null; @@ -486,11 +490,11 @@ class ReactExoplayerView extends FrameLayout implements keyRequestPropertiesArray[i + 1]); } } - return new DefaultDrmSessionManager<>(uuid, + return new DefaultDrmSessionManager(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback, null, false, 3); } - private MediaSource buildMediaSource(Uri uri, String overrideExtension) { + private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { @@ -498,26 +502,30 @@ class ReactExoplayerView extends FrameLayout implements return new SsMediaSource.Factory( new DefaultSsChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_DASH: return new DashMediaSource.Factory( new DefaultDashChunkSource.Factory(mediaDataSourceFactory), buildDataSourceFactory(false) - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_HLS: return new HlsMediaSource.Factory( mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); case C.TYPE_OTHER: return new ProgressiveMediaSource.Factory( mediaDataSourceFactory - ).setLoadErrorHandlingPolicy( + ).setDrmSessionManager(drmSessionManager) + .setLoadErrorHandlingPolicy( config.buildLoadErrorHandlingPolicy(minLoadRetryCount) ).createMediaSource(uri); default: { @@ -1339,23 +1347,23 @@ class ReactExoplayerView extends FrameLayout implements @Override - public void onDrmKeysLoaded() { + public void onDrmKeysLoaded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysLoaded"); } @Override - public void onDrmSessionManagerError(Exception e) { + public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, Exception e) { Log.d("DRM Info", "onDrmSessionManagerError"); eventEmitter.error("onDrmSessionManagerError", e); } @Override - public void onDrmKeysRestored() { + public void onDrmKeysRestored(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysRestored"); } @Override - public void onDrmKeysRemoved() { + public void onDrmKeysRemoved(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { Log.d("DRM Info", "onDrmKeysRemoved"); }