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 ... // 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,9 +32,11 @@
- 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)
- 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) - 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 ### 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) * [Audio Mixing](#audio-mixing)
* [Android Expansion File Usage](#android-expansion-file-usage) * [Android Expansion File Usage](#android-expansion-file-usage)
* [Updating](#updating) * [Updating](#updating)
* [Contributing](#contributing)
## Installation ## Installation
@ -1598,10 +1599,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
} }
} }
@ -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 ## TODOS
- [ ] Add support for playing multiple videos in a sequence (will interfere with current `repeat` implementation) - [ ] 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 { 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

View File

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