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
project.ext {
compileSdkVersion = 23
buildToolsVersion = "23.0.1"
compileSdkVersion = 31
buildToolsVersion = "30.0.2"
minSdkVersion = 16
minSdkVersion = 21
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)
- 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)
- 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)
- 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)

View File

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

View File

@ -5,8 +5,8 @@ def safeExtGet(prop, fallback) {
}
android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
compileSdkVersion safeExtGet('compileSdkVersion', 31)
buildToolsVersion safeExtGet('buildToolsVersion', '30.0.2')
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
@ -14,7 +14,7 @@ android {
}
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16)
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1
versionName "1.0"
@ -33,7 +33,7 @@ repositories {
dependencies {
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'
}
@ -42,7 +42,7 @@ dependencies {
implementation "androidx.core:core: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'
}
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.ForwardingCookieHandler;
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.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.util.Util;
import okhttp3.Call;
import okhttp3.JavaNetCookieJar;
import okhttp3.OkHttpClient;
import java.util.Map;
@ -75,7 +76,7 @@ public class DataSourceUtil {
}
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));
}
@ -84,10 +85,12 @@ public class DataSourceUtil {
CookieJarContainer container = (CookieJarContainer) client.cookieJar();
ForwardingCookieHandler handler = new ForwardingCookieHandler(context);
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)
okHttpDataSourceFactory.getDefaultRequestProperties().set(requestHeaders);
okHttpDataSourceFactory.setDefaultRequestProperties(requestHeaders);
return okHttpDataSourceFactory;
}

View File

@ -13,18 +13,16 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.video.VideoListener;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.ExoPlayer;
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.TextRenderer;
import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.SubtitleView;
import com.google.android.exoplayer2.video.VideoSize;
import java.util.List;
@ -36,7 +34,7 @@ public final class ExoPlayerView extends FrameLayout {
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
private SimpleExoPlayer player;
private ExoPlayer player;
private Context context;
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
* {@link SimpleExoPlayer#addVideoListener} method of the player will be called and previous
* Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
* player will be called and previous
* 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) {
return;
}
if (this.player != null) {
this.player.removeTextOutput(componentListener);
this.player.removeVideoListener(componentListener);
this.player.removeListener(componentListener);
clearVideoView();
}
@ -151,9 +147,7 @@ public final class ExoPlayerView extends FrameLayout {
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
if (player != null) {
setVideoView();
player.addVideoListener(componentListener);
player.addListener(componentListener);
player.addTextOutput(componentListener);
}
}
@ -230,8 +224,7 @@ public final class ExoPlayerView extends FrameLayout {
layout.invalidateAspectRatio();
}
private final class ComponentListener implements VideoListener,
TextOutput, ExoPlayer.EventListener {
private final class ComponentListener implements Player.Listener {
// TextRenderer.Output implementation
@ -240,12 +233,12 @@ public final class ExoPlayerView extends FrameLayout {
subtitleLayout.onCues(cues);
}
// SimpleExoPlayer.VideoListener implementation
// ExoPlayer.VideoListener implementation
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
public void onVideoSizeChanged(VideoSize videoSize) {
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.
if (isInitialRatio) {
@ -261,32 +254,37 @@ public final class ExoPlayerView extends FrameLayout {
// ExoPlayer.EventListener implementation
@Override
public void onLoadingChanged(boolean isLoading) {
public void onIsLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
public void onPlaybackStateChanged(int playbackState) {
// Do nothing.
}
@Override
public void onPlayerError(ExoPlaybackException e) {
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
// Do nothing.
}
@Override
public void onPositionDiscontinuity(int reason) {
public void onPlayerError(PlaybackException e) {
// Do nothing.
}
@Override
public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// Do nothing.
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
public void onTimelineChanged(Timeline timeline, int reason) {
// Do nothing.
}
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
updateForCurrentTrackSelections();
}
@ -295,11 +293,6 @@ public final class ExoPlayerView extends FrameLayout {
// Do nothing
}
@Override
public void onSeekProcessed() {
// Do nothing.
}
@Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// 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.drm.MediaDrmCallbackException;
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.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
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.TracksInfo;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
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.DrmSessionManagerProvider;
import com.google.android.exoplayer2.drm.ExoMediaDrm;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
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.MediaCodecUtil;
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.MediaSource;
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.MappingTrackSelector;
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.upstream.BandwidthMeter;
import com.google.android.exoplayer2.upstream.DataSource;
@ -91,6 +94,7 @@ import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.Map;
@ -108,11 +112,10 @@ import java.lang.Integer;
@SuppressLint("ViewConstructor")
class ReactExoplayerView extends FrameLayout implements
LifecycleEventListener,
Player.EventListener,
Player.Listener,
BandwidthMeter.EventListener,
BecomingNoisyListener,
AudioManager.OnAudioFocusChangeListener,
MetadataOutput,
DrmSessionEventListener {
public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
@ -134,12 +137,12 @@ class ReactExoplayerView extends FrameLayout implements
private final DefaultBandwidthMeter bandwidthMeter;
private PlayerControlView playerControlView;
private View playPauseControlContainer;
private Player.EventListener eventListener;
private Player.Listener eventListener;
private ExoPlayerView exoPlayerView;
private DataSource.Factory mediaDataSourceFactory;
private SimpleExoPlayer player;
private ExoPlayer player;
private DefaultTrackSelector trackSelector;
private boolean playerNeedsSource;
@ -202,7 +205,7 @@ class ReactExoplayerView extends FrameLayout implements
private final AudioManager audioManager;
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
private final Handler progressHandler = new Handler() {
private final Handler progressHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@ -225,7 +228,7 @@ class ReactExoplayerView extends FrameLayout implements
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
Timeline.Window window = new Timeline.Window();
if(!player.getCurrentTimeline().isEmpty()) {
player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window);
player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window);
}
return window.windowStartTimeMs + currentPosition;
}
@ -385,10 +388,17 @@ class ReactExoplayerView extends FrameLayout implements
}
});
// Invoking onPlayerStateChanged event for Player
eventListener = new Player.EventListener() {
// Invoking onPlaybackStateChanged and onPlayWhenReadyChanged events for Player
eventListener = new Player.Listener() {
@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);
//Remove this eventListener once its executed. since UI will work fine once after the reLayout is done
player.removeListener(eventListener);
@ -472,7 +482,7 @@ class ReactExoplayerView extends FrameLayout implements
}
private void startBufferCheckTimer() {
SimpleExoPlayer player = this.player;
Player player = this.player;
VideoEventEmitter eventEmitter = this.eventEmitter;
Handler mainHandler = this.mainHandler;
@ -525,8 +535,6 @@ class ReactExoplayerView extends FrameLayout implements
} else if (srcUri != null) {
initializePlayerSource(self, null);
}
} catch (Exception ex) {
self.playerNeedsSource = true;
Log.e("ExoPlayer Exception", "Failed to initialize Player!");
@ -559,13 +567,12 @@ class ReactExoplayerView extends FrameLayout implements
DefaultRenderersFactory renderersFactory =
new DefaultRenderersFactory(getContext())
.setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF);
player = new SimpleExoPlayer.Builder(getContext(), renderersFactory)
player = new ExoPlayer.Builder(getContext(), renderersFactory)
.setTrackSelector(self.trackSelector)
.setBandwidthMeter(bandwidthMeter)
.setLoadControl(loadControl)
.build();
player.addListener(self);
player.addMetadataOutput(self);
exoPlayerView.setPlayer(player);
audioBecomingNoisyReceiver.setListener(self);
bandwidthMeter.addEventListener(new Handler(), self);
@ -682,37 +689,47 @@ class ReactExoplayerView extends FrameLayout implements
int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
: uri.getLastPathSegment());
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) {
case C.TYPE_SS:
return new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager)
).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
).createMediaSource(mediaItem);
case C.TYPE_DASH:
return new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
buildDataSourceFactory(false)
).setDrmSessionManager(drmSessionManager)
).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
).createMediaSource(mediaItem);
case C.TYPE_HLS:
return new HlsMediaSource.Factory(
mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager)
).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
).createMediaSource(mediaItem);
case C.TYPE_OTHER:
return new ProgressiveMediaSource.Factory(
mediaDataSourceFactory
).setDrmSessionManager(drmSessionManager)
).setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
config.buildLoadErrorHandlingPolicy(minLoadRetryCount)
).createMediaSource(uri);
).createMediaSource(mediaItem);
default: {
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) {
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)
.createMediaSource(uri, textFormat, C.TIME_UNSET);
.createMediaSource(subtitleConfiguration, C.TIME_UNSET);
}
private void releasePlayer() {
if (player != null) {
updateResumePosition();
player.release();
player.removeMetadataOutput(this);
player.removeListener(this);
trackSelector = null;
player = null;
}
@ -831,8 +854,8 @@ class ReactExoplayerView extends FrameLayout implements
}
private void updateResumePosition() {
resumeWindow = player.getCurrentWindowIndex();
resumePosition = player.isCurrentWindowSeekable() ? Math.max(0, player.getCurrentPosition())
resumeWindow = player.getCurrentMediaItemIndex();
resumePosition = player.isCurrentMediaItemSeekable() ? Math.max(0, player.getCurrentPosition())
: C.TIME_UNSET;
}
@ -909,25 +932,28 @@ class ReactExoplayerView extends FrameLayout implements
eventEmitter.audioBecomingNoisy();
}
// Player.EventListener implementation
// Player.Listener implementation
@Override
public void onLoadingChanged(boolean isLoading) {
public void onIsLoadingChanged(boolean isLoading) {
// Do nothing.
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
switch (playbackState) {
case Player.STATE_IDLE:
text += "idle";
eventEmitter.idle();
clearProgressMessageHandler();
if (!playWhenReady) {
setKeepScreenOn(false);
}
break;
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=";
switch (playbackState) {
case Player.STATE_IDLE:
text += "idle";
eventEmitter.idle();
clearProgressMessageHandler();
if (!player.getPlayWhenReady()) {
setKeepScreenOn(false);
}
break;
case Player.STATE_BUFFERING:
text += "buffering";
onBuffering(true);
@ -959,8 +985,8 @@ class ReactExoplayerView extends FrameLayout implements
default:
text += "unknown";
break;
}
}
Log.d(TAG, text);
}
private void startProgressHandler() {
@ -1178,7 +1204,7 @@ class ReactExoplayerView extends FrameLayout implements
}
@Override
public void onPositionDiscontinuity(int reason) {
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
if (playerNeedsSource) {
// 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
@ -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
// 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) {
eventEmitter.end();
}
}
@Override
public void onTimelineChanged(Timeline timeline, int reason) {
// Do nothing.
}
@Override
public void onPlaybackStateChanged(int playbackState) {
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
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
// Do nothing.
@ -1227,8 +1253,8 @@ class ReactExoplayerView extends FrameLayout implements
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
// Do Nothing.
public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Do nothing.
}
@Override
@ -1242,51 +1268,20 @@ class ReactExoplayerView extends FrameLayout implements
}
@Override
public void onPlayerError(ExoPlaybackException e) {
String errorString = "ExoPlaybackException type : " + e.type;
String errorCode = "2001"; // Playback error code 2xxx (2001 - unknown playback exception)
boolean needsReInitialization = false;
Exception ex = e;
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException();
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
// Special case for decoder initialization failures.
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
(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);
}
}
public void onPlayerError(PlaybackException e) {
if (e == null) {
return;
}
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) {
String errorString = "ExoPlaybackException: " + PlaybackException.getErrorCodeName(e.errorCode);
String errorCode = "2" + String.valueOf(e.errorCode);
boolean needsReInitialization = false;
switch(e.errorCode) {
case PlaybackException.ERROR_CODE_DRM_DEVICE_REVOKED:
case PlaybackException.ERROR_CODE_DRM_LICENSE_ACQUISITION_FAILED:
case PlaybackException.ERROR_CODE_DRM_PROVISIONING_FAILED:
case PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR:
case PlaybackException.ERROR_CODE_DRM_UNSPECIFIED:
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
hasDrmFailed = true;
playerNeedsSource = true;
@ -1295,34 +1290,11 @@ class ReactExoplayerView extends FrameLayout implements
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) {
// 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;
}
}
}
break;
default:
break;
}
eventEmitter.error(errorString, ex, errorCode);
eventEmitter.error(errorString, e, errorCode);
playerNeedsSource = true;
if (isBehindLiveWindow(e)) {
clearResumePosition();
@ -1335,20 +1307,8 @@ class ReactExoplayerView extends FrameLayout implements
}
}
private static boolean isBehindLiveWindow(ExoPlaybackException e) {
Log.e("ExoPlayer Exception", e.toString());
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;
private static boolean isBehindLiveWindow(PlaybackException e) {
return e.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW;
}
public int getTrackRendererIndex(int trackType) {
@ -1389,7 +1349,8 @@ class ReactExoplayerView extends FrameLayout implements
public void clearSrc() {
if (srcUri != null) {
player.stop(true);
player.stop();
player.clearMediaItems();
this.srcUri = null;
this.extension = null;
this.requestHeaders = null;
@ -1466,7 +1427,8 @@ class ReactExoplayerView extends FrameLayout implements
TrackGroupArray groups = info.getTrackGroups(rendererIndex);
int groupIndex = C.INDEX_UNSET;
int[] tracks = {0} ;
List<Integer> tracks = new ArrayList<>();
tracks.add(0);
if (TextUtils.isEmpty(type)) {
type = "default";
@ -1511,7 +1473,7 @@ class ReactExoplayerView extends FrameLayout implements
Format format = group.getFormat(j);
if (format.height == height) {
groupIndex = i;
tracks[0] = j;
tracks.set(0, j);
closestFormat = null;
closestTrackIndex = -1;
usingExactMatch = true;
@ -1539,7 +1501,7 @@ class ReactExoplayerView extends FrameLayout implements
if (format.height < minHeight) {
minHeight = format.height;
groupIndex = i;
tracks[0] = j;
tracks.set(0, j);
}
}
}
@ -1547,7 +1509,7 @@ class ReactExoplayerView extends FrameLayout implements
if (closestFormat != null && closestTrackIndex != -1) {
// We found the closest match instead of an exact one
groupIndex = i;
tracks[0] = closestTrackIndex;
tracks.set(0, closestTrackIndex);
}
}
} 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
// Add all tracks as valid options for ABR to choose from
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;
for (int j = 0; j < group.length; j++) {
allTracks[j] = j;
allTracks.add(j);
}
// Valiate list of all tracks and add only supported formats
int supportedFormatLength = 0;
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);
if (isFormatSupported(format)) {
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
tracks = allTracks;
} else {
tracks = new int[supportedFormatLength + 1];
int o = 0;
for (int k = 0; k < allTracks.length; k++) {
tracks = new ArrayList<>(supportedFormatLength + 1);
for (int k = 0; k < allTracks.size(); k++) {
Format format = group.getFormat(k);
if (isFormatSupported(format)) {
tracks[o] = allTracks[k];
supportedTrackList.add(allTracks[k]);
o++;
tracks.add(allTracks.get(k));
supportedTrackList.add(allTracks.get(k));
}
}
}
@ -1602,11 +1562,12 @@ class ReactExoplayerView extends FrameLayout implements
return;
}
TrackSelectionOverride selectionOverride = new TrackSelectionOverride(groups.get(groupIndex), tracks);
DefaultTrackSelector.Parameters selectionParameters = trackSelector.getParameters()
.buildUpon()
.setRendererDisabled(rendererIndex, false)
.setSelectionOverride(rendererIndex, groups,
new DefaultTrackSelector.SelectionOverride(groupIndex, tracks))
.setTrackSelectionOverrides(new TrackSelectionOverrides.Builder().addOverride(selectionOverride).build())
.build();
trackSelector.setParameters(selectionParameters);
}
@ -1694,8 +1655,8 @@ class ReactExoplayerView extends FrameLayout implements
public void seekTo(long positionMs) {
if (player != null) {
seekTime = positionMs;
player.seekTo(positionMs);
eventEmitter.seek(player.getCurrentPosition(), positionMs);
}
}

View File

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

View File

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

View File

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

View File

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