Merge branch 'master' of github.com:react-native-video/react-native-video into type-event-dispatcher-with-protocol

This commit is contained in:
Igor Tironi 2022-06-15 11:01:24 -07:00
commit c2d60a7371
13 changed files with 212 additions and 218 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,9 +32,11 @@
- 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)
- Fixed `onReadyForDisplay` not being called [#2721](https://github.com/react-native-video/react-native-video/pull/2721)
- Fix type of `_eventDispatcher` on iOS target to match `bridge.eventDispatcher()` [#2720](https://github.com/react-native-video/react-native-video/pull/2720)
### Version 5.2.0

31
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,31 @@
## Issues
* New issues are reviewed and if they require additional work will be marked with the [`triage needed`](https://github.com/react-native-video/react-native-video/labels/triage%20needed) label. This is an open call for help from the community to verify the issue and help categorize it. If an issue stays in this state for a long time, it will be closed unresolved.
* Once an issue has been reviewed it will be labeled with [`help wanted`](https://github.com/react-native-video/react-native-video/labels/help%20wanted) to indicate it is ready to be worked on. Please wait for this label before submitting a PR to avoid spending time on something that is likely to be rejected.
## Cleanup
* Given the history of this project, we are going to be more aggressive than usual in keeping things clean. We are working with limited resources and do not want to return to the 1000+ open issues state. This is not meant to be disrespectful or hostile. It is just a way to keep the limited resources we have focused. If your issue was closed prematurely, just chime in and engage!
* Issues and pull requests that become stale (60 days of inactivity) will be closed unless assigned and show progress.
* If the issue creator fails to provide additional information within a week when asked, we may close the issue to keep things tidy (but you can always comment back and we can reopen).
## Pull Requests
* Please open an issue before opening a PR to make sure the proposed change is wanted and is likely to be merged. We don't want you to waste your time!
* Pull requests require 1-3 approved reviews to be merged.
* The number of reviews depends on the complexity by adding up (max of 3):
* `1` reviewer for each PR
* `1` if more than 3 files and/or 30 lines of code changed
* `1` for each native platform code changes involved
For example, a single file JS code change requires 1 review while a 3 files iOS code change requires 3 reviews. As soon as the reviews show up as approved without any requested changes, the PR will be merged into the next milestone.
* Reviewers will be asked to assign a risk level when they are done from 1 (super safe) to 5 (super risky). A release with any risk level 4 or 5 will be published as a major version, otherwise as a patch or minor based on the changes. Prepare for some large version increments while we get more comfortable... (but remember versions are free).
* If you have time to help out, look for the [`review requested`](https://github.com/react-native-video/react-native-video/labels/review%20requested) label. It will have another numeric label with it (`1`, `2`, or `3` indicating how many more reviews are needed to merge).
## Releases
* Aim for a bi-weekly (every other week) release to flush out whatever was approved and merge. Most people use this with a lock file (and if you don't you are doing it wrong) and should not have any issues with new bugs showing up. This is already a high risk dependency which must be tested well before going into production. Let's take advantage of that and move faster.
Please do not harass people to review your pull request! You can tag those you feel have relevant experience but please don't abuse this as people will unfollow or mute the project if they are called too many times!

View File

@ -39,6 +39,7 @@ Version 3.0 features a number of changes to existing behavior. See [Updating](#u
* [Audio Mixing](#audio-mixing)
* [Android Expansion File Usage](#android-expansion-file-usage)
* [Updating](#updating)
* [Contributing](#contributing)
## Installation
@ -1598,10 +1599,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
}
}
@ -1617,6 +1618,9 @@ If your video work on Debug mode, but on Release you see only black screen, plea
>
```
## Contributing
You can find our contribution guidelines [here](CONTRIBUTING.md)
## TODOS
- [ ] Add support for playing multiple videos in a sequence (will interfere with current `repeat` implementation)

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

View File

@ -64,7 +64,7 @@ class RCTPlayerObserver: NSObject {
removePlayerLayerObserver()
}
didSet {
if playerLayer == nil {
if playerLayer != nil {
addPlayerLayerObserver()
}
}