feat: upgrade exoplayer to v2.17.1 (#2498)

Describe the changes
Upgrade ExoPlayer to version 2.17.1

Provide an example of how to test the change
Tested with a forked of react-native-video-test

* Update exoplayer to v2.15.1

* feat: upgrade ExoPlayer to version 2.17.1

* chore: update CHANGELOG

* remove ExoPlayerFullscreenVideoActivity

* Fix build issues

* Fix build & runtime issues

Co-authored-by: Eran Hammer <eran@hammer.io>
Co-authored-by: Armands Malejev <armands.malejevs@gmail.com>
This commit is contained in:
Tai Le Tuan 2022-06-16 00:24:55 +07:00 committed by GitHub
parent 33930c8b9c
commit daf5e595ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 175 additions and 217 deletions

6
API.md
View File

@ -1519,10 +1519,10 @@ allprojects {
... // Various other settings go here ... // Various other settings go here
project.ext { project.ext {
compileSdkVersion = 23 compileSdkVersion = 31
buildToolsVersion = "23.0.1" buildToolsVersion = "30.0.2"
minSdkVersion = 16 minSdkVersion = 21
targetSdkVersion = 22 targetSdkVersion = 22
} }
} }

View File

@ -32,6 +32,7 @@
- Convert iOS implementation to Swift [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Convert iOS implementation to Swift [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Add iOS support for decoding offline sources [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Add iOS support for decoding offline sources [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527) - Update basic example applications (React Native 0.63.4) [#2527](https://github.com/react-native-video/react-native-video/pull/2527)
- Upgrade ExoPlayer to 2.17.1 [#2498](https://github.com/react-native-video/react-native-video/pull/2498)
- Fix volume reset issue in exoPlayer [#2371](https://github.com/react-native-video/react-native-video/pull/2371) - Fix volume reset issue in exoPlayer [#2371](https://github.com/react-native-video/react-native-video/pull/2371)
- Change WindowsTargetPlatformVersion to 10.0 [#2706](https://github.com/react-native-video/react-native-video/pull/2706) - Change WindowsTargetPlatformVersion to 10.0 [#2706](https://github.com/react-native-video/react-native-video/pull/2706)
- Fixed Android seeking bug [#2712](https://github.com/react-native-video/react-native-video/pull/2712) - Fixed Android seeking bug [#2712](https://github.com/react-native-video/react-native-video/pull/2712)

View File

@ -1598,10 +1598,10 @@ allprojects {
... // Various other settings go here ... // Various other settings go here
project.ext { project.ext {
compileSdkVersion = 23 compileSdkVersion = 31
buildToolsVersion = "23.0.1" buildToolsVersion = "30.0.2"
minSdkVersion = 16 minSdkVersion = 21
targetSdkVersion = 22 targetSdkVersion = 22
} }
} }

View File

@ -5,8 +5,8 @@ def safeExtGet(prop, fallback) {
} }
android { android {
compileSdkVersion safeExtGet('compileSdkVersion', 28) compileSdkVersion safeExtGet('compileSdkVersion', 31)
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
compileOptions { compileOptions {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
@ -14,7 +14,7 @@ android {
} }
defaultConfig { defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16) minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28) targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -33,7 +33,7 @@ repositories {
dependencies { dependencies {
implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}"
implementation('com.google.android.exoplayer:exoplayer:2.13.3') { implementation('com.google.android.exoplayer:exoplayer:2.17.1') {
exclude group: 'com.android.support' exclude group: 'com.android.support'
} }
@ -42,7 +42,7 @@ dependencies {
implementation "androidx.core:core:1.1.0" implementation "androidx.core:core:1.1.0"
implementation "androidx.media:media:1.1.0" implementation "androidx.media:media:1.1.0"
implementation('com.google.android.exoplayer:extension-okhttp:2.13.3') { implementation('com.google.android.exoplayer:extension-okhttp:2.17.1') {
exclude group: 'com.squareup.okhttp3', module: 'okhttp' exclude group: 'com.squareup.okhttp3', module: 'okhttp'
} }
implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}' implementation 'com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}'

View File

@ -4,13 +4,14 @@ import com.facebook.react.bridge.ReactContext;
import com.facebook.react.modules.network.CookieJarContainer; import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.ForwardingCookieHandler; import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.OkHttpClientProvider; import com.facebook.react.modules.network.OkHttpClientProvider;
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory; import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import okhttp3.Call;
import okhttp3.JavaNetCookieJar; import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import java.util.Map; import java.util.Map;
@ -75,7 +76,7 @@ public class DataSourceUtil {
} }
private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) { private static DataSource.Factory buildDataSourceFactory(ReactContext context, DefaultBandwidthMeter bandwidthMeter, Map<String, String> requestHeaders) {
return new DefaultDataSourceFactory(context, bandwidthMeter, return new DefaultDataSource.Factory(context,
buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders)); buildHttpDataSourceFactory(context, bandwidthMeter, requestHeaders));
} }
@ -84,10 +85,12 @@ public class DataSourceUtil {
CookieJarContainer container = (CookieJarContainer) client.cookieJar(); CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context); ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
container.setCookieJar(new JavaNetCookieJar(handler)); container.setCookieJar(new JavaNetCookieJar(handler));
OkHttpDataSourceFactory okHttpDataSourceFactory = new OkHttpDataSourceFactory(client, getUserAgent(context), bandwidthMeter); OkHttpDataSource.Factory okHttpDataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) client)
.setUserAgent(getUserAgent(context))
.setTransferListener(bandwidthMeter);
if (requestHeaders != null) if (requestHeaders != null)
okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders); okHttpDataSourceFactory.setDefaultRequestProperties(requestHeaders);
return okHttpDataSourceFactory; return okHttpDataSourceFactory;
} }

View File

@ -13,18 +13,16 @@ import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.SubtitleView; import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.exoplayer2.video.VideoSize;
import java.util.List; import java.util.List;
@ -36,7 +34,7 @@ public final class ExoPlayerView extends FrameLayout {
private final SubtitleView subtitleLayout; private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout; private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private SimpleExoPlayer player; private ExoPlayer player;
private Context context; private Context context;
private ViewGroup.LayoutParams layoutParams; private ViewGroup.LayoutParams layoutParams;
@ -131,19 +129,17 @@ public final class ExoPlayerView extends FrameLayout {
} }
/** /**
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#addTextOutput} and * Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
* {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous * player will be called and previous
* assignments are overridden. * assignments are overridden.
* *
* @param player The {@link SimpleExoPlayer} to use. * @param player The {@link ExoPlayer} to use.
*/ */
public void setPlayer(SimpleExoPlayer player) { public void setPlayer(ExoPlayer player) {
if (this.player == player) { if (this.player == player) {
return; return;
} }
if (this.player != null) { if (this.player != null) {
this.player.removeTextOutput(componentListener);
this.player.removeVideoListener(componentListener);
this.player.removeListener(componentListener); this.player.removeListener(componentListener);
clearVideoView(); clearVideoView();
} }
@ -151,9 +147,7 @@ public final class ExoPlayerView extends FrameLayout {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE); shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
if (player != null) { if (player != null) {
setVideoView(); setVideoView();
player.addVideoListener(componentListener);
player.addListener(componentListener); player.addListener(componentListener);
player.addTextOutput(componentListener);
} }
} }
@ -230,8 +224,7 @@ public final class ExoPlayerView extends FrameLayout {
layout.invalidateAspectRatio(); layout.invalidateAspectRatio();
} }
private final class ComponentListener implements VideoListener, private final class ComponentListener implements Player.Listener {
TextOutput, ExoPlayer.EventListener {
// TextRenderer.Output implementation // TextRenderer.Output implementation
@ -240,12 +233,12 @@ public final class ExoPlayerView extends FrameLayout {
subtitleLayout.onCues(cues); subtitleLayout.onCues(cues);
} }
// SimpleExoPlayer.VideoListener implementation // ExoPlayer.VideoListener implementation
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { public void onVideoSizeChanged(VideoSize videoSize) {
boolean isInitialRatio = layout.getAspectRatio() == 0; boolean isInitialRatio = layout.getAspectRatio() == 0;
layout.setAspectRatio(height == 0 ? 1 : (width * pixelWidthHeightRatio) / height); layout.setAspectRatio(videoSize.height == 0 ? 1 : (videoSize.width * videoSize.pixelWidthHeightRatio) / videoSize.height);
// React native workaround for measuring and layout on initial load. // React native workaround for measuring and layout on initial load.
if (isInitialRatio) { if (isInitialRatio) {
@ -261,32 +254,37 @@ public final class ExoPlayerView extends FrameLayout {
// ExoPlayer.EventListener implementation // ExoPlayer.EventListener implementation
@Override @Override
public void onLoadingChanged(boolean isLoading) { public void onIsLoadingChanged(boolean isLoading) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public void onPlaybackStateChanged(int playbackState) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onPlayerError(ExoPlaybackException e) { public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onPositionDiscontinuity(int reason) { public void onPlayerError(PlaybackException e) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTimelineChanged(Timeline timeline, int reason) {
// Do nothing.
}
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
updateForCurrentTrackSelections(); updateForCurrentTrackSelections();
} }
@ -295,11 +293,6 @@ public final class ExoPlayerView extends FrameLayout {
// Do nothing // Do nothing
} }
@Override
public void onSeekProcessed() {
// Do nothing.
}
@Override @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing. // Do nothing.

View File

@ -35,16 +35,19 @@ import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.drm.MediaDrmCallbackException; import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
import com.google.android.exoplayer2.drm.ExoMediaDrm;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.drm.MediaDrmCallbackException; import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
@ -53,7 +56,6 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.MergingMediaSource;
@ -70,7 +72,8 @@ import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides;
import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride;
import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
@ -91,6 +94,7 @@ import java.net.CookieHandler;
import java.net.CookieManager; import java.net.CookieManager;
import java.net.CookiePolicy; import java.net.CookiePolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.Map; import java.util.Map;
@ -108,11 +112,10 @@ import java.lang.Integer;
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class ReactExoplayerView extends FrameLayout implements class ReactExoplayerView extends FrameLayout implements
LifecycleEventListener, LifecycleEventListener,
Player.EventListener, Player.Listener,
BandwidthMeter.EventListener, BandwidthMeter.EventListener,
BecomingNoisyListener, BecomingNoisyListener,
AudioManager.OnAudioFocusChangeListener, AudioManager.OnAudioFocusChangeListener,
MetadataOutput,
DrmSessionEventListener { DrmSessionEventListener {
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1; public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
@ -134,12 +137,12 @@ class ReactExoplayerView extends FrameLayout implements
private final DefaultBandwidthMeter bandwidthMeter; private final DefaultBandwidthMeter bandwidthMeter;
private PlayerControlView playerControlView; private PlayerControlView playerControlView;
private View playPauseControlContainer; private View playPauseControlContainer;
private Player.EventListener eventListener; private Player.Listener eventListener;
private ExoPlayerView exoPlayerView; private ExoPlayerView exoPlayerView;
private DataSource.Factory mediaDataSourceFactory; private DataSource.Factory mediaDataSourceFactory;
private SimpleExoPlayer player; private ExoPlayer player;
private DefaultTrackSelector trackSelector; private DefaultTrackSelector trackSelector;
private boolean playerNeedsSource; private boolean playerNeedsSource;
@ -202,7 +205,7 @@ class ReactExoplayerView extends FrameLayout implements
private final AudioManager audioManager; private final AudioManager audioManager;
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver; private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
private final Handler progressHandler = new Handler() { private final Handler progressHandler = new Handler(Looper.getMainLooper()) {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
switch (msg.what) { switch (msg.what) {
@ -225,7 +228,7 @@ class ReactExoplayerView extends FrameLayout implements
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) { public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
Timeline.Window window = new Timeline.Window(); Timeline.Window window = new Timeline.Window();
if(!player.getCurrentTimeline().isEmpty()) { if(!player.getCurrentTimeline().isEmpty()) {
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window);
} }
return window.windowStartTimeMs + currentPosition; return window.windowStartTimeMs + currentPosition;
} }
@ -385,10 +388,17 @@ class ReactExoplayerView extends FrameLayout implements
} }
}); });
// Invoking onPlayerStateChanged event for Player // Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player
eventListener = new Player.EventListener() { eventListener = new Player.Listener() {
@Override @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public void onPlaybackStateChanged(int playbackState) {
reLayout(playPauseControlContainer);
//Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
player.removeListener(eventListener);
}
@Override
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
reLayout(playPauseControlContainer); reLayout(playPauseControlContainer);
//Remove this eventListener once its executed. since UI will work fine once after the reLayout is done //Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
player.removeListener(eventListener); player.removeListener(eventListener);
@ -472,7 +482,7 @@ class ReactExoplayerView extends FrameLayout implements
} }
private void startBufferCheckTimer() { private void startBufferCheckTimer() {
SimpleExoPlayer player = this.player; Player player = this.player;
VideoEventEmitter eventEmitter = this.eventEmitter; VideoEventEmitter eventEmitter = this.eventEmitter;
Handler mainHandler = this.mainHandler; Handler mainHandler = this.mainHandler;
@ -525,8 +535,6 @@ class ReactExoplayerView extends FrameLayout implements
} else if (srcUri != null) { } else if (srcUri != null) {
initializePlayerSource(self, null); initializePlayerSource(self, null);
} }
} catch (Exception ex) { } catch (Exception ex) {
self.playerNeedsSource = true; self.playerNeedsSource = true;
Log.e("ExoPlayer Exception", "Failed to initialize Player!"); Log.e("ExoPlayer Exception", "Failed to initialize Player!");
@ -559,13 +567,12 @@ class ReactExoplayerView extends FrameLayout implements
DefaultRenderersFactory renderersFactory = DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext()) new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) player = new ExoPlayer.Builder(getContext(), renderersFactory)
.setTrackSelector(self.trackSelector) .setTrackSelector(self.trackSelector)
.setBandwidthMeter(bandwidthMeter) .setBandwidthMeter(bandwidthMeter)
.setLoadControl(loadControl) .setLoadControl(loadControl)
.build(); .build();
player.addListener(self); player.addListener(self);
player.addMetadataOutput(self);
exoPlayerView.setPlayer(player); exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self); audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self); bandwidthMeter.addEventListener(new Handler(), self);
@ -682,37 +689,47 @@ class ReactExoplayerView extends FrameLayout implements
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment()); : uri.getLastPathSegment());
config.setDisableDisconnectError(this.disableDisconnectError); config.setDisableDisconnectError(this.disableDisconnectError);
MediaItem mediaItem = new MediaItem.Builder().setUri(uri).build();
DrmSessionManagerProvider drmProvider = null;
if (drmSessionManager != null) {
drmProvider = new DrmSessionManagerProvider() {
@Override
public DrmSessionManager get(MediaItem mediaItem) {
return drmSessionManager;
}
};
}
switch (type) { switch (type) {
case C.TYPE_SS: case C.TYPE_SS:
return new SsMediaSource.Factory( return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false) buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager) ).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy( .setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount) config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri); ).createMediaSource(mediaItem);
case C.TYPE_DASH: case C.TYPE_DASH:
return new DashMediaSource.Factory( return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false) buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager) ).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy( .setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount) config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri); ).createMediaSource(mediaItem);
case C.TYPE_HLS: case C.TYPE_HLS:
return new HlsMediaSource.Factory( return new HlsMediaSource.Factory(
mediaDataSourceFactory mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager) ).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy( .setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount) config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri); ).createMediaSource(mediaItem);
case C.TYPE_OTHER: case C.TYPE_OTHER:
return new ProgressiveMediaSource.Factory( return new ProgressiveMediaSource.Factory(
mediaDataSourceFactory mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager) ).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy( .setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount) config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri); ).createMediaSource(mediaItem);
default: { default: {
throw new IllegalStateException("Unsupported type: " + type); throw new IllegalStateException("Unsupported type: " + type);
} }
@ -741,16 +758,22 @@ class ReactExoplayerView extends FrameLayout implements
} }
private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) { private MediaSource buildTextSource(String title, Uri uri, String mimeType, String language) {
Format textFormat = Format.createTextSampleFormat(title, mimeType, Format.NO_VALUE, language); MediaItem.SubtitleConfiguration subtitleConfiguration = new MediaItem.SubtitleConfiguration.Builder(uri)
.setMimeType(mimeType)
.setLanguage(language)
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.setRoleFlags(C.ROLE_FLAG_SUBTITLE)
.setLabel(title)
.build();
return new SingleSampleMediaSource.Factory(mediaDataSourceFactory) return new SingleSampleMediaSource.Factory(mediaDataSourceFactory)
.createMediaSource(uri, textFormat, C.TIME_UNSET); .createMediaSource(subtitleConfiguration, C.TIME_UNSET);
} }
private void releasePlayer() { private void releasePlayer() {
if (player != null) { if (player != null) {
updateResumePosition(); updateResumePosition();
player.release(); player.release();
player.removeMetadataOutput(this); player.removeListener(this);
trackSelector = null; trackSelector = null;
player = null; player = null;
} }
@ -831,8 +854,8 @@ class ReactExoplayerView extends FrameLayout implements
} }
private void updateResumePosition() { private void updateResumePosition() {
resumeWindow = player.getCurrentWindowIndex(); resumeWindow = player.getCurrentMediaItemIndex();
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition()) resumePosition = player.isCurrentMediaItemSeekable() ? Math.max(0, player.getCurrentPosition())
: C.TIME_UNSET; : C.TIME_UNSET;
} }
@ -909,22 +932,25 @@ class ReactExoplayerView extends FrameLayout implements
eventEmitter.audioBecomingNoisy(); eventEmitter.audioBecomingNoisy();
} }
// Player.EventListener implementation // Player.Listener implementation
@Override @Override
public void onLoadingChanged(boolean isLoading) { public void onIsLoadingChanged(boolean isLoading) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { public void onEvents(Player player, Player.Events events) {
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
int playbackState = player.getPlaybackState();
boolean playWhenReady = player.getPlayWhenReady();
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState="; String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
switch (playbackState) { switch (playbackState) {
case Player.STATE_IDLE: case Player.STATE_IDLE:
text += "idle"; text += "idle";
eventEmitter.idle(); eventEmitter.idle();
clearProgressMessageHandler(); clearProgressMessageHandler();
if (!playWhenReady) { if (!player.getPlayWhenReady()) {
setKeepScreenOn(false); setKeepScreenOn(false);
} }
break; break;
@ -960,7 +986,7 @@ class ReactExoplayerView extends FrameLayout implements
text += "unknown"; text += "unknown";
break; break;
} }
Log.d(TAG, text); }
} }
private void startProgressHandler() { private void startProgressHandler() {
@ -1178,7 +1204,7 @@ class ReactExoplayerView extends FrameLayout implements
} }
@Override @Override
public void onPositionDiscontinuity(int reason) { public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
if (playerNeedsSource) { if (playerNeedsSource) {
// This will only occur if the user has performed a seek whilst in the error state. Update the // This will only occur if the user has performed a seek whilst in the error state. Update the
// resume position so that if the user then retries, playback will resume from the position to // resume position so that if the user then retries, playback will resume from the position to
@ -1192,13 +1218,18 @@ class ReactExoplayerView extends FrameLayout implements
} }
// When repeat is turned on, reaching the end of the video will not cause a state change // When repeat is turned on, reaching the end of the video will not cause a state change
// so we need to explicitly detect it. // so we need to explicitly detect it.
if (reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) { && player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
eventEmitter.end(); eventEmitter.end();
} }
} }
@Override
public void onTimelineChanged(Timeline timeline, int reason) {
// Do nothing.
}
@Override @Override
public void onPlaybackStateChanged(int playbackState) { public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_READY && seekTime != C.TIME_UNSET) { if (playbackState == Player.STATE_READY && seekTime != C.TIME_UNSET) {
@ -1211,11 +1242,6 @@ class ReactExoplayerView extends FrameLayout implements
} }
} }
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
// Do nothing.
}
@Override @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing. // Do nothing.
@ -1227,8 +1253,8 @@ class ReactExoplayerView extends FrameLayout implements
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Do Nothing. // Do nothing.
} }
@Override @Override
@ -1242,74 +1268,19 @@ class ReactExoplayerView extends FrameLayout implements
} }
@Override @Override
public void onPlayerError(ExoPlaybackException e) { public void onPlayerError(PlaybackException e) {
String errorString = "ExoPlaybackException type : " + e.type; if (e == null) {
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception) return;
}
String errorString = "ExoPlaybackException: " + PlaybackException.getErrorCodeName(e.errorCode);
String errorCode = "2" + String.valueOf(e.errorCode);
boolean needsReInitialization = false; boolean needsReInitialization = false;
Exception ex = e; switch(e.errorCode) {
if (e.type == ExoPlaybackException.TYPE_RENDERER) { case PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED:
Exception cause = e.getRendererException(); case PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED:
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { case PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED:
// Special case for decoder initialization failures. case PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR:
MediaCodecRenderer.DecoderInitializationException decoderInitializationException = case PlaybackException.ERROR_CODE_DRM_UNSPECIFIED:
(MediaCodecRenderer.DecoderInitializationException) cause;
if (decoderInitializationException.codecInfo == null
|| decoderInitializationException.codecInfo.name == null) {
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
errorCode = "2011";
errorString = getResources().getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorCode = "2012";
errorString = getResources().getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorCode = "2013";
errorString = getResources().getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorCode = "2014";
errorString = getResources().getString(R.string.error_instantiating_decoder,
decoderInitializationException.codecInfo.name);
}
}
}
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
// Re-initialization improves recovery speed and properly resumes
needsReInitialization = true;
errorString = getResources().getString(R.string.unrecognized_media_format);
Exception cause = e.getSourceException();
if (cause instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
errorCode = "3004";
errorString = getResources().getString(R.string.unrecognized_media_format);
} else if(cause instanceof MediaDrmCallbackException || cause instanceof DrmSessionException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
// DrmSessionExceptions can be caused by a lot internal reasons for failure, in most cases they can be safely retried and playback will recover
if (!hasDrmFailed || cause instanceof DrmSessionException) {
// When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time
hasDrmFailed = true;
playerNeedsSource = true;
updateResumePosition();
initializePlayer();
setPlayWhenReady(true);
return;
}
} else if (cause instanceof HttpDataSource.HttpDataSourceException) {
// this exception happens when connectivity is lost
updateResumePosition();
initializePlayer();
setPlayWhenReady(true);
return;
} else {
errorCode = "2021";
errorString = getResources().getString(R.string.unrecognized_media_format);
}
if (cause != null) {
Throwable rootCause = cause.getCause();
if (rootCause instanceof MediaDrmCallbackException) {
errorCode = "3005";
errorString = getResources().getString(R.string.unrecognized_media_format);
if (!hasDrmFailed) { if (!hasDrmFailed) {
// When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time // When DRM fails to reach the app level certificate server it will fail with a source error so we assume that it is DRM related and try one more time
hasDrmFailed = true; hasDrmFailed = true;
@ -1319,10 +1290,11 @@ class ReactExoplayerView extends FrameLayout implements
setPlayWhenReady(true); setPlayWhenReady(true);
return; return;
} }
break;
default:
break;
} }
} eventEmitter.error(errorString, e, errorCode);
}
eventEmitter.error(errorString, ex, errorCode);
playerNeedsSource = true; playerNeedsSource = true;
if (isBehindLiveWindow(e)) { if (isBehindLiveWindow(e)) {
clearResumePosition(); clearResumePosition();
@ -1335,20 +1307,8 @@ class ReactExoplayerView extends FrameLayout implements
} }
} }
private static boolean isBehindLiveWindow(ExoPlaybackException e) { private static boolean isBehindLiveWindow(PlaybackException e) {
Log.e("ExoPlayer Exception", e.toString()); return e.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW;
if (e.type != ExoPlaybackException.TYPE_SOURCE) {
return false;
}
Throwable cause = e.getSourceException();
while (cause != null) {
if (cause instanceof BehindLiveWindowException ||
cause instanceof HttpDataSource.HttpDataSourceException) {
return true;
}
cause = cause.getCause();
}
return false;
} }
public int getTrackRendererIndex(int trackType) { public int getTrackRendererIndex(int trackType) {
@ -1389,7 +1349,8 @@ class ReactExoplayerView extends FrameLayout implements
public void clearSrc() { public void clearSrc() {
if (srcUri != null) { if (srcUri != null) {
player.stop(true); player.stop();
player.clearMediaItems();
this.srcUri = null; this.srcUri = null;
this.extension = null; this.extension = null;
this.requestHeaders = null; this.requestHeaders = null;
@ -1466,7 +1427,8 @@ class ReactExoplayerView extends FrameLayout implements
TrackGroupArray groups = info.getTrackGroups(rendererIndex); TrackGroupArray groups = info.getTrackGroups(rendererIndex);
int groupIndex = C.INDEX_UNSET; int groupIndex = C.INDEX_UNSET;
int[] tracks = {0} ; List<Integer> tracks = new ArrayList<>();
tracks.add(0);
if (TextUtils.isEmpty(type)) { if (TextUtils.isEmpty(type)) {
type = "default"; type = "default";
@ -1511,7 +1473,7 @@ class ReactExoplayerView extends FrameLayout implements
Format format = group.getFormat(j); Format format = group.getFormat(j);
if (format.height == height) { if (format.height == height) {
groupIndex = i; groupIndex = i;
tracks[0] = j; tracks.set(0, j);
closestFormat = null; closestFormat = null;
closestTrackIndex = -1; closestTrackIndex = -1;
usingExactMatch = true; usingExactMatch = true;
@ -1539,7 +1501,7 @@ class ReactExoplayerView extends FrameLayout implements
if (format.height < minHeight) { if (format.height < minHeight) {
minHeight = format.height; minHeight = format.height;
groupIndex = i; groupIndex = i;
tracks[0] = j; tracks.set(0, j);
} }
} }
} }
@ -1547,7 +1509,7 @@ class ReactExoplayerView extends FrameLayout implements
if (closestFormat != null && closestTrackIndex != -1) { if (closestFormat != null && closestTrackIndex != -1) {
// We found the closest match instead of an exact one // We found the closest match instead of an exact one
groupIndex = i; groupIndex = i;
tracks[0] = closestTrackIndex; tracks.set(0, closestTrackIndex);
} }
} }
} else if (trackType == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default } else if (trackType == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
@ -1564,34 +1526,32 @@ class ReactExoplayerView extends FrameLayout implements
if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto if (groupIndex == C.INDEX_UNSET && trackType == C.TRACK_TYPE_VIDEO && groups.length != 0) { // Video auto
// Add all tracks as valid options for ABR to choose from // Add all tracks as valid options for ABR to choose from
TrackGroup group = groups.get(0); TrackGroup group = groups.get(0);
int[] allTracks = new int[group.length]; tracks = new ArrayList<Integer>(group.length);
ArrayList<Integer> allTracks = new ArrayList<Integer>(group.length);
groupIndex = 0; groupIndex = 0;
for (int j = 0; j < group.length; j++) { for (int j = 0; j < group.length; j++) {
allTracks[j] = j; allTracks.add(j);
} }
// Valiate list of all tracks and add only supported formats // Valiate list of all tracks and add only supported formats
int supportedFormatLength = 0; int supportedFormatLength = 0;
ArrayList<Integer> supportedTrackList = new ArrayList<Integer>(); ArrayList<Integer> supportedTrackList = new ArrayList<Integer>();
for (int g = 0; g < allTracks.length; g++) { for (int g = 0; g < allTracks.size(); g++) {
Format format = group.getFormat(g); Format format = group.getFormat(g);
if (isFormatSupported(format)) { if (isFormatSupported(format)) {
supportedFormatLength++; supportedFormatLength++;
} }
} }
if (allTracks.length == 1) { if (allTracks.size() == 1) {
// With only one tracks we can't remove any tracks so attempt to play it anyway // With only one tracks we can't remove any tracks so attempt to play it anyway
tracks = allTracks; tracks = allTracks;
} else { } else {
tracks = new int[supportedFormatLength + 1]; tracks = new ArrayList<>(supportedFormatLength + 1);
int o = 0; for (int k = 0; k < allTracks.size(); k++) {
for (int k = 0; k < allTracks.length; k++) {
Format format = group.getFormat(k); Format format = group.getFormat(k);
if (isFormatSupported(format)) { if (isFormatSupported(format)) {
tracks[o] = allTracks[k]; tracks.add(allTracks.get(k));
supportedTrackList.add(allTracks[k]); supportedTrackList.add(allTracks.get(k));
o++;
} }
} }
} }
@ -1602,11 +1562,12 @@ class ReactExoplayerView extends FrameLayout implements
return; return;
} }
TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters() DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
.buildUpon() .buildUpon()
.setRendererDisabled(rendererIndex, false) .setRendererDisabled(rendererIndex, false)
.setSelectionOverride(rendererIndex, groups, .setTrackSelectionOverrides(new TrackSelectionOverrides.Builder().addOverride(selectionOverride).build())
new DefaultTrackSelector.SelectionOverride(groupIndex, tracks))
.build(); .build();
trackSelector.setParameters(selectionParameters); trackSelector.setParameters(selectionParameters);
} }
@ -1694,8 +1655,8 @@ class ReactExoplayerView extends FrameLayout implements
public void seekTo(long positionMs) { public void seekTo(long positionMs) {
if (player != null) { if (player != null) {
seekTime = positionMs;
player.seekTo(positionMs); player.seekTo(positionMs);
eventEmitter.seek(player.getCurrentPosition(), positionMs);
} }
} }

View File

@ -5,11 +5,11 @@ def safeExtGet(prop, fallback) {
} }
android { android {
compileSdkVersion safeExtGet('compileSdkVersion', 28) compileSdkVersion safeExtGet('compileSdkVersion', 31)
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
defaultConfig { defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16) minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28) targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@ -2,9 +2,9 @@
buildscript { buildscript {
ext { ext {
buildToolsVersion = "29.0.2" buildToolsVersion = "30.0.2"
minSdkVersion = 16 minSdkVersion = 21
compileSdkVersion = 29 compileSdkVersion = 31
targetSdkVersion = 29 targetSdkVersion = 29
} }
repositories { repositories {

View File

@ -94,12 +94,12 @@ def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false def enableProguardInReleaseBuilds = false
android { android {
compileSdkVersion 23 compileSdkVersion 31
buildToolsVersion "23.0.1" buildToolsVersion "30.0.2"
defaultConfig { defaultConfig {
applicationId "com.videocaching" applicationId "com.videocaching"
minSdkVersion 16 minSdkVersion 21
targetSdkVersion 22 targetSdkVersion 22
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"

View File

@ -7,7 +7,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-sdk <uses-sdk
android:minSdkVersion="16" android:minSdkVersion="21"
android:targetSdkVersion="22" /> android:targetSdkVersion="22" />
<application <application