diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ebc569..dada0845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Changelog +### next +* Handle racing conditions when props are setted on exoplayer + ### Version 4.4.3 * Fix mute/unmute when controls are present (iOS) [#1654](https://github.com/react-native-community/react-native-video/pull/1654) * Fix Android videos being able to play with background music/audio from other apps. 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 bea3e20e..1edb8c7a 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -144,6 +144,7 @@ class ReactExoplayerView extends FrameLayout implements private boolean playInBackground = false; private Map requestHeaders; private boolean mReportBandwidth = false; + private boolean controls; // \ End props // React @@ -267,6 +268,7 @@ class ReactExoplayerView extends FrameLayout implements * Toggling the visibility of the player control view */ private void togglePlayerControlVisibility() { + if(player == null) return; reLayout(playerControlView); if (playerControlView.isVisible()) { playerControlView.hide(); @@ -312,10 +314,15 @@ class ReactExoplayerView extends FrameLayout implements * Adding Player control to the frame layout */ private void addPlayerControl() { + if(player == null) return; LayoutParams layoutParams = new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); playerControlView.setLayoutParams(layoutParams); + int indexOfPC = indexOfChild(playerControlView); + if (indexOfPC != -1) { + removeViewAt(indexOfPC); + } addView(playerControlView, 1, layoutParams); } @@ -333,53 +340,62 @@ class ReactExoplayerView extends FrameLayout implements } private void initializePlayer() { - if (player == null) { - TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); - trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); - trackSelector.setParameters(trackSelector.buildUponParameters() + ReactExoplayerView self = this; + // This ensures all props have been setted, to avoid async racing conditions. + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if (player == null) { + TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); + trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); + trackSelector.setParameters(trackSelector.buildUponParameters() .setMaxVideoBitrate(maxBitRate == 0 ? Integer.MAX_VALUE : maxBitRate)); - DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); - DefaultLoadControl defaultLoadControl = new DefaultLoadControl(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, -1, true); - player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, defaultLoadControl); - player.addListener(this); - player.setMetadataOutput(this); - exoPlayerView.setPlayer(player); - audioBecomingNoisyReceiver.setListener(this); - BANDWIDTH_METER.addEventListener(new Handler(), this); - setPlayWhenReady(!isPaused); - playerNeedsSource = true; + DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); + DefaultLoadControl defaultLoadControl = new DefaultLoadControl(allocator, minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs, -1, true); + player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, defaultLoadControl); + player.addListener(self); + player.setMetadataOutput(self); + exoPlayerView.setPlayer(player); + audioBecomingNoisyReceiver.setListener(self); + BANDWIDTH_METER.addEventListener(new Handler(), self); + setPlayWhenReady(!isPaused); + playerNeedsSource = true; - PlaybackParameters params = new PlaybackParameters(rate, 1f); - player.setPlaybackParameters(params); - } - if (playerNeedsSource && srcUri != null) { - ArrayList mediaSourceList = buildTextSources(); - MediaSource videoSource = buildMediaSource(srcUri, extension); - MediaSource mediaSource; - if (mediaSourceList.size() == 0) { - mediaSource = videoSource; - } else { - mediaSourceList.add(0, videoSource); - MediaSource[] textSourceArray = mediaSourceList.toArray( - new MediaSource[mediaSourceList.size()] - ); - mediaSource = new MergingMediaSource(textSourceArray); + PlaybackParameters params = new PlaybackParameters(rate, 1f); + player.setPlaybackParameters(params); + } + if (playerNeedsSource && srcUri != null) { + ArrayList mediaSourceList = buildTextSources(); + MediaSource videoSource = buildMediaSource(srcUri, extension); + MediaSource mediaSource; + if (mediaSourceList.size() == 0) { + mediaSource = videoSource; + } else { + mediaSourceList.add(0, videoSource); + MediaSource[] textSourceArray = mediaSourceList.toArray( + new MediaSource[mediaSourceList.size()] + ); + mediaSource = new MergingMediaSource(textSourceArray); + } + + boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; + if (haveResumePosition) { + player.seekTo(resumeWindow, resumePosition); + } + player.prepare(mediaSource, !haveResumePosition, false); + playerNeedsSource = false; + + eventEmitter.loadStart(); + loadVideoStarted = true; + } + + // Initializing the playerControlView + initializePlayerControl(); + setControls(controls); + applyModifiers(); } - - boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; - if (haveResumePosition) { - player.seekTo(resumeWindow, resumePosition); - } - player.prepare(mediaSource, !haveResumePosition, false); - playerNeedsSource = false; - - eventEmitter.loadStart(); - loadVideoStarted = true; - } - - // Initializing the playerControlView - initializePlayerControl(); + }, 1); } private MediaSource buildMediaSource(Uri uri, String overrideExtension) { @@ -439,8 +455,8 @@ class ReactExoplayerView extends FrameLayout implements updateResumePosition(); player.release(); player.setMetadataOutput(null); - player = null; trackSelector = null; + player = null; } progressHandler.removeMessages(SHOW_PROGRESS); themedReactContext.removeLifecycleEventListener(this); @@ -894,6 +910,10 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setResizeMode(resizeMode); } + private void applyModifiers() { + setRepeatModifier(repeat); + } + public void setRepeatModifier(boolean repeat) { if (player != null) { if (repeat) { @@ -906,6 +926,7 @@ class ReactExoplayerView extends FrameLayout implements } public void setSelectedTrack(int trackType, String type, Dynamic value) { + if (player == null) return; int rendererIndex = getTrackRendererIndex(trackType); if (rendererIndex == C.INDEX_UNSET) { return; @@ -1156,10 +1177,15 @@ class ReactExoplayerView extends FrameLayout implements * @param controls Controls prop, if true enable controls, if false disable them */ public void setControls(boolean controls) { - if (controls && exoPlayerView != null) { + this.controls = controls; + if (player == null || exoPlayerView == null) return; + if (controls) { addPlayerControl(); - } else if (getChildAt(1) instanceof PlayerControlView && exoPlayerView != null) { - removeViewAt(1); + } else { + int indexOfPC = indexOfChild(playerControlView); + if (indexOfPC != -1) { + removeViewAt(indexOfPC); + } } } }