NuvioStreaming/patches/react-native-video+6.18.0.patch
2026-01-06 00:25:18 +05:30

1323 lines
72 KiB
Diff

diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 539ecfd..a939148 100644
--- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -161,7 +161,7 @@ public class ReactExoplayerView extends FrameLayout implements
AdEvent.AdEventListener,
AdErrorEvent.AdErrorListener {
- public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1;
+ public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 0.5;
public static final double DEFAULT_MIN_BUFFER_MEMORY_RESERVE = 0;
private static final String TAG = "ReactExoplayerView";
@@ -222,7 +222,8 @@ public class ReactExoplayerView extends FrameLayout implements
private ArrayList<Integer> rootViewChildrenOriginalVisibility = new ArrayList<Integer>();
/*
- * When user is seeking first called is on onPositionDiscontinuity -> DISCONTINUITY_REASON_SEEK
+ * When user is seeking first called is on onPositionDiscontinuity ->
+ * DISCONTINUITY_REASON_SEEK
* Then we set if to false when playback is back in onIsPlayingChanged -> true
*/
private boolean isSeeking = false;
@@ -243,7 +244,7 @@ public class ReactExoplayerView extends FrameLayout implements
private BufferingStrategy.BufferingStrategyEnum bufferingStrategy;
private boolean disableDisconnectError;
private boolean preventsDisplaySleepDuringVideoPlayback = true;
- private float mProgressUpdateInterval = 250.0f;
+ private float mProgressUpdateInterval = 1000.0f;
protected boolean playInBackground = false;
private boolean mReportBandwidth = false;
private boolean controls = false;
@@ -269,6 +270,7 @@ public class ReactExoplayerView extends FrameLayout implements
private final String instanceId = String.valueOf(UUID.randomUUID());
private CmcdConfiguration.Factory cmcdConfigurationFactory;
+ private static final ExecutorService SHARED_EXECUTOR = Executors.newSingleThreadExecutor();
public void setCmcdConfigurationFactory(CmcdConfiguration.Factory factory) {
this.cmcdConfigurationFactory = factory;
@@ -292,7 +294,8 @@ public class ReactExoplayerView extends FrameLayout implements
lastPos = pos;
lastBufferDuration = bufferedDuration;
lastDuration = duration;
- eventEmitter.onVideoProgress.invoke(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos));
+ eventEmitter.onVideoProgress.invoke(pos, bufferedDuration, player.getDuration(),
+ getPositionInFirstPeriodMsForCurrentWindow(pos));
}
}
}
@@ -310,7 +313,7 @@ public class ReactExoplayerView extends FrameLayout implements
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
Timeline.Window window = new Timeline.Window();
- if(!player.getCurrentTimeline().isEmpty()) {
+ if (!player.getCurrentTimeline().isEmpty()) {
player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window);
}
return window.windowStartTimeMs + currentPosition;
@@ -349,9 +352,9 @@ public class ReactExoplayerView extends FrameLayout implements
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
exoPlayerView = new ExoPlayerView(getContext());
- exoPlayerView.addOnLayoutChangeListener( (View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) ->
- PictureInPictureUtil.applySourceRectHint(themedReactContext, pictureInPictureParamsBuilder, exoPlayerView)
- );
+ exoPlayerView.addOnLayoutChangeListener(
+ (View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) -> PictureInPictureUtil
+ .applySourceRectHint(themedReactContext, pictureInPictureParamsBuilder, exoPlayerView));
exoPlayerView.setLayoutParams(layoutParams);
addView(exoPlayerView, 0, layoutParams);
@@ -377,8 +380,10 @@ public class ReactExoplayerView extends FrameLayout implements
public void onHostPause() {
isInBackground = true;
Activity activity = themedReactContext.getCurrentActivity();
- boolean isInPictureInPicture = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null && activity.isInPictureInPictureMode();
- boolean isInMultiWindowMode = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null && activity.isInMultiWindowMode();
+ boolean isInPictureInPicture = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null
+ && activity.isInPictureInPictureMode();
+ boolean isInMultiWindowMode = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null
+ && activity.isInMultiWindowMode();
if (playInBackground || isInPictureInPicture || isInMultiWindowMode) {
return;
}
@@ -397,7 +402,7 @@ public class ReactExoplayerView extends FrameLayout implements
viewHasDropped = true;
}
- //BandwidthMeter.EventListener implementation
+ // BandwidthMeter.EventListener implementation
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
if (mReportBandwidth) {
@@ -405,7 +410,8 @@ public class ReactExoplayerView extends FrameLayout implements
eventEmitter.onVideoBandwidthUpdate.invoke(bitrate, 0, 0, null);
} else {
Format videoFormat = player.getVideoFormat();
- boolean isRotatedContent = videoFormat != null && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
+ boolean isRotatedContent = videoFormat != null
+ && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
int width = videoFormat != null ? (isRotatedContent ? videoFormat.height : videoFormat.width) : 0;
int height = videoFormat != null ? (isRotatedContent ? videoFormat.width : videoFormat.height) : 0;
String trackId = videoFormat != null ? videoFormat.id : null;
@@ -420,7 +426,8 @@ public class ReactExoplayerView extends FrameLayout implements
* Toggling the visibility of the player control view
*/
private void togglePlayerControlVisibility() {
- if (player == null) return;
+ if (player == null)
+ return;
if (exoPlayerView.isControllerVisible()) {
exoPlayerView.hideController();
} else {
@@ -444,7 +451,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void updateControllerConfig() {
- if (exoPlayerView == null) return;
+ if (exoPlayerView == null)
+ return;
exoPlayerView.setControllerShowTimeoutMs(5000);
@@ -455,7 +463,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void updateControllerVisibility() {
- if (exoPlayerView == null) return;
+ if (exoPlayerView == null)
+ return;
exoPlayerView.setUseController(controls && !controlsConfig.getHideFullscreen());
}
@@ -463,7 +472,7 @@ public class ReactExoplayerView extends FrameLayout implements
private void openSettings() {
AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext);
builder.setTitle(R.string.settings);
- String[] settingsOptions = {themedReactContext.getString(R.string.playback_speed)};
+ String[] settingsOptions = { themedReactContext.getString(R.string.playback_speed) };
builder.setItems(settingsOptions, (dialog, which) -> {
if (which == 0) {
showPlaybackSpeedOptions();
@@ -473,7 +482,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void showPlaybackSpeedOptions() {
- String[] speedOptions = {"0.5x", "1.0x", "1.5x", "2.0x"};
+ String[] speedOptions = { "0.5x", "1.0x", "1.5x", "2.0x" };
AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext);
builder.setTitle(R.string.select_playback_speed);
@@ -491,8 +500,10 @@ public class ReactExoplayerView extends FrameLayout implements
speed = 2.0f;
break;
default:
- speed = 1.0f;;
- };
+ speed = 1.0f;
+ ;
+ }
+ ;
setRateModifier(speed);
});
builder.show();
@@ -504,24 +515,30 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Update the layout
- * @param view view needs to update layout
*
- * This is a workaround for the open bug in react-native: <a href="https://github.com/facebook/react-native/issues/17968">...</a>
+ * @param view view needs to update layout
+ *
+ * This is a workaround for the open bug in react-native: <a href=
+ * "https://github.com/facebook/react-native/issues/17968">...</a>
*/
private void reLayout(View view) {
- if (view == null) return;
+ if (view == null)
+ return;
view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight());
}
private void refreshControlsStyles() {
- if (exoPlayerView == null || player == null || !controls) return;
+ if (exoPlayerView == null || player == null || !controls)
+ return;
updateControllerVisibility();
}
- // Note: The following methods for live content and button visibility are no longer needed
- // as PlayerView handles controls automatically. Some functionality may need to be
+ // Note: The following methods for live content and button visibility are no
+ // longer needed
+ // as PlayerView handles controls automatically. Some functionality may need to
+ // be
// reimplemented using PlayerView's APIs if custom behavior is required.
private void reLayoutControls() {
@@ -558,6 +575,7 @@ public class ReactExoplayerView extends FrameLayout implements
private class RNVLoadControl extends DefaultLoadControl {
private final int availableHeapInBytes;
private final Runtime runtime;
+
public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) {
super(allocator,
config.getMinBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
@@ -568,7 +586,7 @@ public class ReactExoplayerView extends FrameLayout implements
: DefaultLoadControl.DEFAULT_MAX_BUFFER_MS,
config.getBufferForPlaybackMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBufferForPlaybackMs()
- : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS ,
+ : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
config.getBufferForPlaybackAfterRebufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBufferForPlaybackAfterRebufferMs()
: DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
@@ -579,10 +597,12 @@ public class ReactExoplayerView extends FrameLayout implements
: DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME);
runtime = Runtime.getRuntime();
- ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
- double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
- ? config.getMaxHeapAllocationPercent()
- : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
+ ActivityManager activityManager = (ActivityManager) themedReactContext
+ .getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
+ double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion
+ .getBufferConfigPropUnsetDouble()
+ ? config.getMaxHeapAllocationPercent()
+ : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024);
}
@@ -600,13 +620,15 @@ public class ReactExoplayerView extends FrameLayout implements
}
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory;
- double minBufferMemoryReservePercent = source.getBufferConfig().getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
- ? source.getBufferConfig().getMinBufferMemoryReservePercent()
- : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
+ double minBufferMemoryReservePercent = source.getBufferConfig()
+ .getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
+ ? source.getBufferConfig().getMinBufferMemoryReservePercent()
+ : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
long reserveMemory = (long) minBufferMemoryReservePercent * runtime.maxMemory();
long bufferedMs = bufferedDurationUs / (long) 1000;
if (reserveMemory > freeMemory && bufferedMs > 2000) {
- // We don't have enough memory in reserve so we stop buffering to allow other components to use it instead
+ // We don't have enough memory in reserve so we stop buffering to allow other
+ // components to use it instead
return false;
}
if (runtime.freeMemory() == 0) {
@@ -621,6 +643,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void initializePlayer() {
+ drmRetryCount = 0;
+ hasDrmFailed = false;
disableCache = ReactNativeVideoManager.Companion.getInstance().shouldDisableCache(source);
ReactExoplayerView self = this;
@@ -640,12 +664,16 @@ public class ReactExoplayerView extends FrameLayout implements
// Initialize core configuration and listeners
initializePlayerCore(self);
pipListenerUnsubscribe = PictureInPictureUtil.addLifecycleEventListener(themedReactContext, this);
- PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder, this.enterPictureInPictureOnLeave);
+ PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder,
+ this.enterPictureInPictureOnLeave);
}
- if (!source.isLocalAssetFile() && !source.isAsset() && source.getBufferConfig().getCacheSize() > 0) {
+ long requestedCacheSize = source.getBufferConfig().getCacheSize();
+ long MAX_SAFE_CACHE_SIZE = 100L * 1024 * 1024;
+ long effectiveCacheSize = Math.min(requestedCacheSize, MAX_SAFE_CACHE_SIZE);
+ if (!source.isLocalAssetFile() && !source.isAsset() && effectiveCacheSize > 0) {
RNVSimpleCache.INSTANCE.setSimpleCache(
this.getContext(),
- source.getBufferConfig().getCacheSize()
+ effectiveCacheSize
);
useCache = true;
} else {
@@ -654,16 +682,17 @@ public class ReactExoplayerView extends FrameLayout implements
if (playerNeedsSource) {
// Will force display of shutter view if needed
exoPlayerView.invalidateAspectRatio();
- // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
- ExecutorService es = Executors.newSingleThreadExecutor();
- es.execute(() -> {
+ // DRM session manager creation must be done on a different thread to prevent
+ // crashes so we start a new thread
+ SHARED_EXECUTOR.execute(() -> {
// DRM initialization must run on a different thread
if (viewHasDropped && runningSource == source) {
return;
}
if (activity == null) {
DebugLog.e(TAG, "Failed to initialize Player!, null activity");
- eventEmitter.onVideoError.invoke("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001");
+ eventEmitter.onVideoError.invoke("Failed to initialize Player!",
+ new Exception("Current Activity is null!"), "1001");
return;
}
@@ -716,8 +745,7 @@ public class ReactExoplayerView extends FrameLayout implements
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl(
allocator,
- source.getBufferConfig()
- );
+ source.getBufferConfig());
long initialBitrate = source.getBufferConfig().getInitialBitrate();
if (initialBitrate > 0) {
@@ -725,15 +753,15 @@ public class ReactExoplayerView extends FrameLayout implements
this.bandwidthMeter = config.getBandwidthMeter();
}
- DefaultRenderersFactory renderersFactory =
- new DefaultRenderersFactory(getContext())
- .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF)
- .setEnableDecoderFallback(true)
- .forceEnableMediaCodecAsynchronousQueueing();
+ DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext())
+ .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
+ .setEnableDecoderFallback(true)
+ .forceEnableMediaCodecAsynchronousQueueing();
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
if (useCache && !disableCache) {
- mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
+ mediaSourceFactory
+ .setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
}
mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView.getPlayerView());
@@ -760,7 +788,7 @@ public class ReactExoplayerView extends FrameLayout implements
player.setPlaybackParameters(params);
changeAudioOutput(this.audioOutput);
- if(showNotificationControls) {
+ if (showNotificationControls) {
setupPlaybackService();
}
}
@@ -772,8 +800,7 @@ public class ReactExoplayerView extends FrameLayout implements
Uri adTagUrl = adProps.getAdTagUrl();
if (adTagUrl != null) {
// Create an AdsLoader.
- ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader
- .Builder(themedReactContext)
+ ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader.Builder(themedReactContext)
.setAdEventListener(this)
.setAdErrorListener(this);
@@ -805,7 +832,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
try {
- // First check if there's a custom DRM manager registered through the plugin system
+ // First check if there's a custom DRM manager registered through the plugin
+ // system
DRMManagerSpec drmManager = ReactNativeVideoManager.Companion.getInstance().getDRMManager();
if (drmManager == null) {
// If no custom manager is registered, use the default implementation
@@ -814,11 +842,13 @@ public class ReactExoplayerView extends FrameLayout implements
DrmSessionManager drmSessionManager = drmManager.buildDrmSessionManager(uuid, drmProps);
if (drmSessionManager == null) {
- eventEmitter.onVideoError.invoke("Failed to build DRM session manager", new Exception("DRM session manager is null"), "3007");
+ eventEmitter.onVideoError.invoke("Failed to build DRM session manager",
+ new Exception("DRM session manager is null"), "3007");
}
// Allow plugins to override the DrmSessionManager
- DrmSessionManager overriddenManager = ReactNativeVideoManager.Companion.getInstance().overrideDrmSessionManager(source, drmSessionManager);
+ DrmSessionManager overriddenManager = ReactNativeVideoManager.Companion.getInstance()
+ .overrideDrmSessionManager(source, drmSessionManager);
return overriddenManager != null ? overriddenManager : drmSessionManager;
} catch (UnsupportedDrmException ex) {
// Unsupported DRM exceptions are handled by the calling method
@@ -836,7 +866,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
/// init DRM
DrmSessionManager drmSessionManager = initializePlayerDrm();
- if (drmSessionManager == null && runningSource.getDrmProps() != null && runningSource.getDrmProps().getDrmType() != null) {
+ if (drmSessionManager == null && runningSource.getDrmProps() != null
+ && runningSource.getDrmProps().getDrmType() != null) {
// Failed to initialize DRM session manager - cannot continue
DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!");
return;
@@ -851,13 +882,10 @@ public class ReactExoplayerView extends FrameLayout implements
MediaSource mediaSource = Objects.requireNonNullElse(mediaSourceWithAds, videoSource);
// wait for player to be set
- while (player == null) {
- try {
- wait();
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- DebugLog.e(TAG, ex.toString());
- }
+ if (player == null) {
+ DebugLog.w(TAG, "Player not ready yet, aborting source initialization");
+ playerNeedsSource = true;
+ return;
}
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET;
@@ -893,7 +921,8 @@ public class ReactExoplayerView extends FrameLayout implements
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
- ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
+ ? R.string.error_drm_unsupported_scheme
+ : R.string.error_drm_unknown);
eventEmitter.onVideoError.invoke(getResources().getString(errorStringId), e, "3003");
}
}
@@ -938,7 +967,8 @@ public class ReactExoplayerView extends FrameLayout implements
if (playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
}
- } catch (Exception ignored) {}
+ } catch (Exception ignored) {
+ }
playbackServiceBinder = null;
}
@@ -970,21 +1000,22 @@ public class ReactExoplayerView extends FrameLayout implements
private void cleanupPlaybackService() {
try {
- if(player != null && playbackServiceBinder != null) {
+ if (player != null && playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
}
playbackServiceBinder = null;
- if(playbackServiceConnection != null) {
+ if (playbackServiceConnection != null) {
themedReactContext.unbindService(playbackServiceConnection);
}
- } catch(Exception e) {
+ } catch (Exception e) {
DebugLog.w(TAG, "Cloud not cleanup playback service");
}
}
- private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager, long cropStartMs, long cropEndMs) {
+ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager,
+ long cropStartMs, long cropEndMs) {
if (uri == null) {
throw new IllegalStateException("Invalid video uri");
}
@@ -1016,12 +1047,12 @@ public class ReactExoplayerView extends FrameLayout implements
Uri adTagUrl = source.getAdsProps().getAdTagUrl();
if (adTagUrl != null) {
mediaItemBuilder.setAdsConfiguration(
- new MediaItem.AdsConfiguration.Builder(adTagUrl).build()
- );
+ new MediaItem.AdsConfiguration.Builder(adTagUrl).build());
}
}
- MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(source.getBufferConfig());
+ MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils
+ .getLiveConfiguration(source.getBufferConfig());
mediaItemBuilder.setLiveConfiguration(liveConfiguration.build());
MediaSource.Factory mediaSourceFactory;
@@ -1033,29 +1064,26 @@ public class ReactExoplayerView extends FrameLayout implements
drmProvider = new DefaultDrmSessionManagerProvider();
}
-
switch (type) {
case CONTENT_TYPE_SS:
- if(!BuildConfig.USE_EXOPLAYER_SMOOTH_STREAMING) {
+ if (!BuildConfig.USE_EXOPLAYER_SMOOTH_STREAMING) {
DebugLog.e("Exo Player Exception", "Smooth Streaming is not enabled!");
throw new IllegalStateException("Smooth Streaming is not enabled!");
}
mediaSourceFactory = new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
- buildDataSourceFactory(false)
- );
+ buildDataSourceFactory(false));
break;
case CONTENT_TYPE_DASH:
- if(!BuildConfig.USE_EXOPLAYER_DASH) {
+ if (!BuildConfig.USE_EXOPLAYER_DASH) {
DebugLog.e("Exo Player Exception", "DASH is not enabled!");
throw new IllegalStateException("DASH is not enabled!");
}
mediaSourceFactory = new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
- buildDataSourceFactory(false)
- );
+ buildDataSourceFactory(false));
break;
case CONTENT_TYPE_HLS:
if (!BuildConfig.USE_EXOPLAYER_HLS) {
@@ -1070,13 +1098,14 @@ public class ReactExoplayerView extends FrameLayout implements
}
mediaSourceFactory = new HlsMediaSource.Factory(
- dataSourceFactory
- ).setAllowChunklessPreparation(source.getTextTracksAllowChunklessPreparation());
+ dataSourceFactory)
+ .setAllowChunklessPreparation(source.getTextTracksAllowChunklessPreparation());
break;
case CONTENT_TYPE_OTHER:
if ("asset".equals(uri.getScheme())) {
try {
- DataSource.Factory assetDataSourceFactory = DataSourceUtil.buildAssetDataSourceFactory(themedReactContext, uri);
+ DataSource.Factory assetDataSourceFactory = DataSourceUtil
+ .buildAssetDataSourceFactory(themedReactContext, uri);
mediaSourceFactory = new ProgressiveMediaSource.Factory(assetDataSourceFactory);
} catch (Exception e) {
throw new IllegalStateException("cannot open input file:" + uri);
@@ -1084,12 +1113,10 @@ public class ReactExoplayerView extends FrameLayout implements
} else if ("file".equals(uri.getScheme()) ||
!useCache) {
mediaSourceFactory = new ProgressiveMediaSource.Factory(
- mediaDataSourceFactory
- );
+ mediaDataSourceFactory);
} else {
mediaSourceFactory = new ProgressiveMediaSource.Factory(
- RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true))
- );
+ RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
}
break;
@@ -1108,20 +1135,19 @@ public class ReactExoplayerView extends FrameLayout implements
if (cmcdConfigurationFactory != null) {
mediaSourceFactory = mediaSourceFactory.setCmcdConfigurationFactory(
- cmcdConfigurationFactory::createCmcdConfiguration
- );
+ cmcdConfigurationFactory::createCmcdConfiguration);
}
mediaSourceFactory = Objects.requireNonNullElse(
ReactNativeVideoManager.Companion.getInstance()
.overrideMediaSourceFactory(source, mediaSourceFactory, mediaDataSourceFactory),
- mediaSourceFactory
- );
+ mediaSourceFactory);
mediaItemBuilder.setStreamKeys(streamKeys);
@Nullable
- final MediaItem.Builder overridenMediaItemBuilder = ReactNativeVideoManager.Companion.getInstance().overrideMediaItemBuilder(source, mediaItemBuilder);
+ final MediaItem.Builder overridenMediaItemBuilder = ReactNativeVideoManager.Companion.getInstance()
+ .overrideMediaItemBuilder(source, mediaItemBuilder);
MediaItem mediaItem = overridenMediaItemBuilder != null
? overridenMediaItemBuilder.build()
@@ -1130,8 +1156,7 @@ public class ReactExoplayerView extends FrameLayout implements
MediaSource mediaSource = mediaSourceFactory
.setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
- config.buildLoadErrorHandlingPolicy(source.getMinLoadRetryCount())
- )
+ config.buildLoadErrorHandlingPolicy(source.getMinLoadRetryCount()))
.createMediaSource(mediaItem);
if (cropStartMs >= 0 && cropEndMs >= 0) {
@@ -1166,7 +1191,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
}
- MediaItem.SubtitleConfiguration.Builder configBuilder = new MediaItem.SubtitleConfiguration.Builder(track.getUri())
+ MediaItem.SubtitleConfiguration.Builder configBuilder = new MediaItem.SubtitleConfiguration.Builder(
+ track.getUri())
.setId(trackId)
.setMimeType(track.getType())
.setLabel(label)
@@ -1177,7 +1203,8 @@ public class ReactExoplayerView extends FrameLayout implements
configBuilder.setLanguage(track.getLanguage());
}
- // Set selection flags - make first track default if no specific track is selected
+ // Set selection flags - make first track default if no specific track is
+ // selected
if (trackIndex == 0 && (textTrackType == null || "disabled".equals(textTrackType))) {
configBuilder.setSelectionFlags(C.SELECTION_FLAG_DEFAULT);
} else {
@@ -1187,10 +1214,12 @@ public class ReactExoplayerView extends FrameLayout implements
MediaItem.SubtitleConfiguration subtitleConfiguration = configBuilder.build();
subtitleConfigurations.add(subtitleConfiguration);
- DebugLog.d(TAG, "Created subtitle configuration: " + trackId + " - " + label + " (" + track.getType() + ")");
+ DebugLog.d(TAG,
+ "Created subtitle configuration: " + trackId + " - " + label + " (" + track.getType() + ")");
trackIndex++;
} catch (Exception e) {
- DebugLog.e(TAG, "Error creating SubtitleConfiguration for URI " + track.getUri() + ": " + e.getMessage());
+ DebugLog.e(TAG,
+ "Error creating SubtitleConfiguration for URI " + track.getUri() + ": " + e.getMessage());
}
}
@@ -1203,7 +1232,7 @@ public class ReactExoplayerView extends FrameLayout implements
private void releasePlayer() {
if (player != null) {
- if(playbackServiceBinder != null) {
+ if (playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
themedReactContext.unbindService(playbackServiceConnection);
}
@@ -1253,7 +1282,8 @@ public class ReactExoplayerView extends FrameLayout implements
case AudioManager.AUDIOFOCUS_LOSS:
view.hasAudioFocus = false;
view.eventEmitter.onAudioFocusChanged.invoke(false);
- // FIXME this pause can cause issue if content doesn't have pause capability (can happen on live channel)
+ // FIXME this pause can cause issue if content doesn't have pause capability
+ // (can happen on live channel)
if (activity != null) {
activity.runOnUiThread(view::pausePlayback);
}
@@ -1274,16 +1304,12 @@ public class ReactExoplayerView extends FrameLayout implements
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume
if (!view.muted) {
- activity.runOnUiThread(() ->
- view.player.setVolume(view.audioVolume * 0.8f)
- );
+ activity.runOnUiThread(() -> view.player.setVolume(view.audioVolume * 0.8f));
}
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Raise it back to normal
if (!view.muted) {
- activity.runOnUiThread(() ->
- view.player.setVolume(view.audioVolume * 1)
- );
+ activity.runOnUiThread(() -> view.player.setVolume(view.audioVolume * 1));
}
}
}
@@ -1356,7 +1382,8 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Returns a new DataSource factory.
*
- * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener
+ * to the new
* DataSource factory.
* @return A new DataSource factory.
*/
@@ -1368,12 +1395,14 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Returns a new HttpDataSource factory.
*
- * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
- * DataSource factory.
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener
+ * to the new
+ * DataSource factory.
* @return A new HttpDataSource factory.
*/
private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) {
- return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, source.getHeaders());
+ return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext,
+ useBandwidthMeter ? bandwidthMeter : null, source.getHeaders());
}
// AudioBecomingNoisyListener implementation
@@ -1390,11 +1419,13 @@ public class ReactExoplayerView extends FrameLayout implements
@Override
public void onEvents(@NonNull Player player, Player.Events events) {
- if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
+ 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=";
- eventEmitter.onPlaybackRateChange.invoke(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f);
+ eventEmitter.onPlaybackRateChange
+ .invoke(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f);
switch (playbackState) {
case Player.STATE_IDLE:
text += "idle";
@@ -1451,9 +1482,11 @@ public class ReactExoplayerView extends FrameLayout implements
}
/**
- * The progress message handler will duplicate recursions of the onProgressMessage handler
- * on change of player state from any state to STATE_READY with playWhenReady is true (when
- * the video is not paused). This clears all existing messages.
+ * The progress message handler will duplicate recursions of the
+ * onProgressMessage handler
+ * on change of player state from any state to STATE_READY with playWhenReady is
+ * true (when
+ * the video is not paused). This clears all existing messages.
*/
private void clearProgressMessageHandler() {
progressHandler.removeMessages(SHOW_PROGRESS);
@@ -1472,7 +1505,8 @@ public class ReactExoplayerView extends FrameLayout implements
setSelectedTextTrack(textTrackType, textTrackValue);
}
Format videoFormat = player.getVideoFormat();
- boolean isRotatedContent = videoFormat != null && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
+ boolean isRotatedContent = videoFormat != null
+ && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
int width = videoFormat != null ? (isRotatedContent ? videoFormat.height : videoFormat.width) : 0;
int height = videoFormat != null ? (isRotatedContent ? videoFormat.width : videoFormat.height) : 0;
String trackId = videoFormat != null ? videoFormat.id : null;
@@ -1481,18 +1515,18 @@ public class ReactExoplayerView extends FrameLayout implements
long duration = player.getDuration();
long currentPosition = player.getCurrentPosition();
ArrayList<Track> audioTracks = getAudioTrackInfo();
- ArrayList<Track> textTracks = getTextTrackInfo();
+ ArrayList<Track> textTracks = getTextTrackInfo();
if (source.getContentStartTime() != -1) {
- ExecutorService es = Executors.newSingleThreadExecutor();
- es.execute(() -> {
- // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done
+ SHARED_EXECUTOR.execute(() -> {
+ // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread
+ // and notify the player only when we're done
ArrayList<VideoTrack> videoTracks = getVideoTrackInfoFromManifest();
if (videoTracks != null) {
isUsingContentResolution = true;
}
eventEmitter.onVideoLoad.invoke(duration, currentPosition, width, height,
- audioTracks, textTracks, videoTracks, trackId );
+ audioTracks, textTracks, videoTracks, trackId);
updateSubtitleButtonVisibility();
});
@@ -1510,9 +1544,9 @@ public class ReactExoplayerView extends FrameLayout implements
}
private static boolean isTrackSelected(TrackSelection selection, TrackGroup group,
- int trackIndex){
+ int trackIndex) {
return selection != null && selection.getTrackGroup() == group
- && selection.indexOf( trackIndex ) != C.INDEX_UNSET;
+ && selection.indexOf(trackIndex) != C.INDEX_UNSET;
}
private ArrayList<Track> getAudioTrackInfo() {
@@ -1530,7 +1564,6 @@ public class ReactExoplayerView extends FrameLayout implements
TrackSelectionArray selectionArray = player.getCurrentTrackSelections();
TrackSelection selection = selectionArray.get(C.TRACK_TYPE_AUDIO);
-
for (int groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
TrackGroup group = groups.get(groupIndex);
Format format = group.getFormat(0);
@@ -1556,7 +1589,8 @@ public class ReactExoplayerView extends FrameLayout implements
videoTrack.setHeight(format.height == Format.NO_VALUE ? 0 : format.height);
videoTrack.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
videoTrack.setRotation(format.rotationDegrees);
- if (format.codecs != null) videoTrack.setCodecs(format.codecs);
+ if (format.codecs != null)
+ videoTrack.setCodecs(format.codecs);
videoTrack.setTrackId(format.id == null ? String.valueOf(trackIndex) : format.id);
videoTrack.setIndex(trackIndex);
return videoTrack;
@@ -1593,33 +1627,35 @@ public class ReactExoplayerView extends FrameLayout implements
return this.getVideoTrackInfoFromManifest(0);
}
- // We need retry count to in case where minefest request fails from poor network conditions
+ // We need retry count to in case where minefest request fails from poor network
+ // conditions
@WorkerThread
private ArrayList<VideoTrack> getVideoTrackInfoFromManifest(int retryCount) {
- ExecutorService es = Executors.newSingleThreadExecutor();
final DataSource dataSource = this.mediaDataSourceFactory.createDataSource();
final Uri sourceUri = source.getUri();
final long startTime = source.getContentStartTime() * 1000 - 100; // s -> ms with 100ms offset
- Future<ArrayList<VideoTrack>> result = es.submit(new Callable() {
+ Future<ArrayList<VideoTrack>> result = SHARED_EXECUTOR.submit(new Callable<ArrayList<VideoTrack>>() {
final DataSource ds = dataSource;
final Uri uri = sourceUri;
final long startTimeUs = startTime * 1000; // ms -> us
public ArrayList<VideoTrack> call() {
ArrayList<VideoTrack> videoTracks = new ArrayList<>();
- try {
+ try {
DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri);
int periodCount = manifest.getPeriodCount();
for (int i = 0; i < periodCount; i++) {
Period period = manifest.getPeriod(i);
- for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets.size(); adaptationIndex++) {
+ for (int adaptationIndex = 0; adaptationIndex < period.adaptationSets
+ .size(); adaptationIndex++) {
AdaptationSet adaptation = period.adaptationSets.get(adaptationIndex);
if (adaptation.type != C.TRACK_TYPE_VIDEO) {
continue;
}
boolean hasFoundContentPeriod = false;
- for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) {
+ for (int representationIndex = 0; representationIndex < adaptation.representations
+ .size(); representationIndex++) {
Representation representation = adaptation.representations.get(representationIndex);
Format format = representation.format;
if (isFormatSupported(format)) {
@@ -1627,7 +1663,8 @@ public class ReactExoplayerView extends FrameLayout implements
break;
}
hasFoundContentPeriod = true;
- VideoTrack videoTrack = exoplayerVideoTrackToGenericVideoTrack(format, representationIndex);
+ VideoTrack videoTrack = exoplayerVideoTrackToGenericVideoTrack(format,
+ representationIndex);
videoTracks.add(videoTrack);
}
}
@@ -1648,7 +1685,6 @@ public class ReactExoplayerView extends FrameLayout implements
if (results == null && retryCount < 1) {
return this.getVideoTrackInfoFromManifest(++retryCount);
}
- es.shutdown();
return results;
} catch (Exception e) {
DebugLog.w(TAG, "error in getVideoTrackInfoFromManifest handling request:" + e.getMessage());
@@ -1657,12 +1693,16 @@ public class ReactExoplayerView extends FrameLayout implements
return null;
}
- private Track exoplayerTrackToGenericTrack(Format format, int trackIndex, TrackSelection selection, TrackGroup group) {
+ private Track exoplayerTrackToGenericTrack(Format format, int trackIndex, TrackSelection selection,
+ TrackGroup group) {
Track track = new Track();
track.setIndex(trackIndex);
- if (format.sampleMimeType != null) track.setMimeType(format.sampleMimeType);
- if (format.language != null) track.setLanguage(format.language);
- if (format.label != null) track.setTitle(format.label);
+ if (format.sampleMimeType != null)
+ track.setMimeType(format.sampleMimeType);
+ if (format.language != null)
+ track.setLanguage(format.language);
+ if (format.label != null)
+ track.setTitle(format.label);
track.setSelected(isTrackSelected(selection, group, trackIndex));
return track;
}
@@ -1732,7 +1772,8 @@ public class ReactExoplayerView extends FrameLayout implements
track.setLanguage(format.language != null ? format.language : "unknown");
track.setTitle(format.label != null ? format.label : "Track " + (groupIndex + 1));
track.setSelected(false); // Don't report selection status - let PlayerView handle it
- if (format.sampleMimeType != null) track.setMimeType(format.sampleMimeType);
+ if (format.sampleMimeType != null)
+ track.setMimeType(format.sampleMimeType);
track.setBitrate(format.bitrate == Format.NO_VALUE ? 0 : format.bitrate);
tracks.add(track);
@@ -1763,8 +1804,10 @@ public class ReactExoplayerView extends FrameLayout implements
Track textTrack = new Track();
textTrack.setIndex(textTracks.size());
- if (format.sampleMimeType != null) textTrack.setMimeType(format.sampleMimeType);
- if (format.language != null) textTrack.setLanguage(format.language);
+ if (format.sampleMimeType != null)
+ textTrack.setMimeType(format.sampleMimeType);
+ if (format.language != null)
+ textTrack.setLanguage(format.language);
boolean isExternal = format.id != null && format.id.startsWith("external-subtitle-");
@@ -1798,28 +1841,34 @@ public class ReactExoplayerView extends FrameLayout implements
}
@Override
- public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, @Player.DiscontinuityReason int reason) {
+ public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition,
+ @NonNull Player.PositionInfo newPosition, @Player.DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_SEEK) {
isSeeking = true;
seekPosition = newPosition.positionMs;
if (isUsingContentResolution) {
- // We need to update the selected track to make sure that it still matches user selection if track list has changed in this period
+ // We need to update the selected track to make sure that it still matches user
+ // selection if track list has changed in this period
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
}
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
+ // 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
// which they seeked.
updateResumePosition();
}
if (isUsingContentResolution) {
- // Discontinuity events might have a different track list so we update the selected track
+ // Discontinuity events might have a different track list so we update the
+ // selected track
setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
selectTrackWhenReady = true;
}
- // 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.
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION
&& player.getRepeatMode() == Player.REPEAT_MODE_ONE) {
@@ -1867,15 +1916,17 @@ public class ReactExoplayerView extends FrameLayout implements
updateSubtitleButtonVisibility();
}
-
private boolean hasBuiltInTextTracks() {
- if (player == null || trackSelector == null) return false;
+ if (player == null || trackSelector == null)
+ return false;
MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo();
- if (info == null) return false;
+ if (info == null)
+ return false;
int textRendererIndex = getTrackRendererIndex(C.TRACK_TYPE_TEXT);
- if (textRendererIndex == C.INDEX_UNSET) return false;
+ if (textRendererIndex == C.INDEX_UNSET)
+ return false;
TrackGroupArray groups = info.getTrackGroups(textRendererIndex);
@@ -1895,11 +1946,12 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void updateSubtitleButtonVisibility() {
- if (exoPlayerView == null) return;
+ if (exoPlayerView == null)
+ return;
boolean hasTextTracks = (source.getSideLoadedTextTracks() != null &&
- !source.getSideLoadedTextTracks().getTracks().isEmpty()) ||
- hasBuiltInTextTracks();
+ !source.getSideLoadedTextTracks().getTracks().isEmpty()) ||
+ hasBuiltInTextTracks();
exoPlayerView.setShowSubtitleButton(hasTextTracks);
}
@@ -1919,7 +1971,8 @@ public class ReactExoplayerView extends FrameLayout implements
if (isPlaying && isSeeking) {
eventEmitter.onVideoSeek.invoke(player.getCurrentPosition(), seekPosition);
}
- PictureInPictureUtil.applyPlayingStatus(themedReactContext, pictureInPictureParamsBuilder, pictureInPictureReceiver, !isPlaying);
+ PictureInPictureUtil.applyPlayingStatus(themedReactContext, pictureInPictureParamsBuilder,
+ pictureInPictureReceiver, !isPlaying);
eventEmitter.onVideoPlaybackStateChanged.invoke(isPlaying, isSeeking);
if (isPlaying) {
@@ -1931,21 +1984,24 @@ public class ReactExoplayerView extends FrameLayout implements
public void onPlayerError(@NonNull PlaybackException e) {
String errorString = "ExoPlaybackException: " + PlaybackException.getErrorCodeName(e.errorCode);
String errorCode = "2" + e.errorCode;
- switch(e.errorCode) {
+ 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;
- updateResumePosition();
- initializePlayer();
- setPlayWhenReady(true);
- return;
- }
+ // 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
+ if (drmRetryCount < 1) {
+ drmRetryCount++;
+ hasDrmFailed = true;
+ playerNeedsSource = true;
+ updateResumePosition();
+ initializePlayer();
+ setPlayWhenReady(true);
+ return;
+ }
break;
default:
break;
@@ -2020,14 +2076,16 @@ public class ReactExoplayerView extends FrameLayout implements
boolean isSourceEqual = source.isEquals(this.source);
hasDrmFailed = false;
this.source = source;
- final DataSource.Factory tmpMediaDataSourceFactory =
- DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
- source.getHeaders());
+ final DataSource.Factory tmpMediaDataSourceFactory = DataSourceUtil.getDefaultDataSourceFactory(
+ this.themedReactContext, bandwidthMeter,
+ source.getHeaders());
@Nullable
- final DataSource.Factory overriddenMediaDataSourceFactory = ReactNativeVideoManager.Companion.getInstance().overrideMediaDataSourceFactory(source, tmpMediaDataSourceFactory);
+ final DataSource.Factory overriddenMediaDataSourceFactory = ReactNativeVideoManager.Companion.getInstance()
+ .overrideMediaDataSourceFactory(source, tmpMediaDataSourceFactory);
- this.mediaDataSourceFactory = Objects.requireNonNullElse(overriddenMediaDataSourceFactory, tmpMediaDataSourceFactory);
+ this.mediaDataSourceFactory = Objects.requireNonNullElse(overriddenMediaDataSourceFactory,
+ tmpMediaDataSourceFactory);
if (source.getCmcdProps() != null) {
CMCDConfig cmcdConfig = new CMCDConfig(source.getCmcdProps());
@@ -2046,6 +2104,7 @@ public class ReactExoplayerView extends FrameLayout implements
clearSrc();
}
}
+
public void clearSrc() {
if (source.getUri() != null) {
if (player != null) {
@@ -2094,7 +2153,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
public void disableTrack(int rendererIndex) {
- if (trackSelector == null) return;
+ if (trackSelector == null)
+ return;
DefaultTrackSelector.Parameters disableParameters = trackSelector.getParameters()
.buildUpon()
@@ -2104,7 +2164,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void selectTextTrackInternal(String type, String value) {
- if (player == null || trackSelector == null) return;
+ if (player == null || trackSelector == null)
+ return;
DebugLog.d(TAG, "selectTextTrackInternal: type=" + type + ", value=" + value);
@@ -2123,6 +2184,7 @@ public class ReactExoplayerView extends FrameLayout implements
if (textRendererIndex != C.INDEX_UNSET) {
TrackGroupArray groups = info.getTrackGroups(textRendererIndex);
boolean trackFound = false;
+ int cumulativeIndex = 0; // Track cumulative index across all groups
for (int groupIndex = 0; groupIndex < groups.length; groupIndex++) {
TrackGroup group = groups.get(groupIndex);
@@ -2136,25 +2198,28 @@ public class ReactExoplayerView extends FrameLayout implements
isMatch = true;
} else if ("index".equals(type)) {
int targetIndex = ReactBridgeUtils.safeParseInt(value, -1);
- if (targetIndex == trackIndex) {
+ // Use cumulative index to match getTextTrackInfo() behavior
+ if (targetIndex == cumulativeIndex) {
isMatch = true;
}
}
if (isMatch) {
TrackSelectionOverride override = new TrackSelectionOverride(group,
- java.util.Arrays.asList(trackIndex));
+ java.util.Arrays.asList(trackIndex));
parametersBuilder.addOverride(override);
trackFound = true;
break;
}
+ cumulativeIndex++; // Increment after each track
}
- if (trackFound) break;
+ if (trackFound)
+ break;
}
if (!trackFound) {
DebugLog.w(TAG, "Text track not found for type=" + type + ", value=" + value +
- ". Keeping current selection.");
+ ". Keeping current selection.");
}
}
}
@@ -2175,7 +2240,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
public void setSelectedTrack(int trackType, String type, String value) {
- if (player == null || trackSelector == null) return;
+ if (player == null || trackSelector == null)
+ return;
if (controls) {
return;
@@ -2249,9 +2315,11 @@ public class ReactExoplayerView extends FrameLayout implements
usingExactMatch = true;
break;
} else if (isUsingContentResolution) {
- // When using content resolution rather than ads, we need to try and find the closest match if there is no exact match
+ // When using content resolution rather than ads, we need to try and find the
+ // closest match if there is no exact match
if (closestFormat != null) {
- if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height) && format.height < height) {
+ if ((format.bitrate > closestFormat.bitrate || format.height > closestFormat.height)
+ && format.height < height) {
// Higher quality match
closestFormat = format;
closestTrackIndex = j;
@@ -2262,7 +2330,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
}
}
- // This is a fallback if the new period contains only higher resolutions than the user has selected
+ // This is a fallback if the new period contains only higher resolutions than
+ // the user has selected
if (closestFormat == null && isUsingContentResolution && !usingExactMatch) {
// No close match found - so we pick the lowest quality
int minHeight = Integer.MAX_VALUE;
@@ -2285,8 +2354,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
} else if (trackType == C.TRACK_TYPE_TEXT && Util.SDK_INT > 18) { // Text default
// Use system settings if possible
- CaptioningManager captioningManager
- = (CaptioningManager)themedReactContext.getSystemService(Context.CAPTIONING_SERVICE);
+ CaptioningManager captioningManager = (CaptioningManager) themedReactContext
+ .getSystemService(Context.CAPTIONING_SERVICE);
if (captioningManager != null && captioningManager.isEnabled()) {
groupIndex = getGroupIndexForDefaultLocale(groups);
}
@@ -2315,7 +2384,7 @@ public class ReactExoplayerView extends FrameLayout implements
// With only one tracks we can't remove any tracks so attempt to play it anyway
tracks = allTracks;
} else {
- tracks = new ArrayList<>(supportedFormatLength + 1);
+ tracks = new ArrayList<>(supportedFormatLength + 1);
for (int k = 0; k < allTracks.size(); k++) {
Format format = group.getFormat(k);
if (isFormatSupported(format)) {
@@ -2341,7 +2410,8 @@ public class ReactExoplayerView extends FrameLayout implements
.setRendererDisabled(rendererIndex, false);
// Clear existing overrides for this track type to avoid conflicts
- // But be careful with audio tracks - don't clear unless explicitly selecting a different track
+ // But be careful with audio tracks - don't clear unless explicitly selecting a
+ // different track
if (trackType != C.TRACK_TYPE_AUDIO || !type.equals("default")) {
selectionParameters.clearOverridesOfType(selectionOverride.getType());
}
@@ -2357,7 +2427,7 @@ public class ReactExoplayerView extends FrameLayout implements
selectionParameters.setForceHighestSupportedBitrate(false);
selectionParameters.setForceLowestBitrate(false);
DebugLog.d(TAG, "Audio track selection: group=" + groupIndex + ", tracks=" + tracks +
- ", override=" + selectionOverride);
+ ", override=" + selectionOverride);
}
trackSelector.setParameters(selectionParameters.build());
@@ -2388,7 +2458,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
private int getGroupIndexForDefaultLocale(TrackGroupArray groups) {
- if (groups.length == 0){
+ if (groups.length == 0) {
return C.INDEX_UNSET;
}
@@ -2409,7 +2479,8 @@ public class ReactExoplayerView extends FrameLayout implements
public void setSelectedVideoTrack(String type, String value) {
videoTrackType = type;
videoTrackValue = value;
- if (!loadVideoStarted) setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
+ if (!loadVideoStarted)
+ setSelectedTrack(C.TRACK_TYPE_VIDEO, videoTrackType, videoTrackValue);
}
public void setSelectedAudioTrack(String type, String value) {
@@ -2440,9 +2511,11 @@ public class ReactExoplayerView extends FrameLayout implements
}
public void setEnterPictureInPictureOnLeave(boolean enterPictureInPictureOnLeave) {
- this.enterPictureInPictureOnLeave = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && enterPictureInPictureOnLeave;
+ this.enterPictureInPictureOnLeave = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+ && enterPictureInPictureOnLeave;
if (player != null) {
- PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder, this.enterPictureInPictureOnLeave);
+ PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder,
+ this.enterPictureInPictureOnLeave);
}
}
@@ -2450,12 +2523,14 @@ public class ReactExoplayerView extends FrameLayout implements
eventEmitter.onPictureInPictureStatusChanged.invoke(isInPictureInPicture);
if (fullScreenPlayerView != null && fullScreenPlayerView.isShowing()) {
- if (isInPictureInPicture) fullScreenPlayerView.hideWithoutPlayer();
+ if (isInPictureInPicture)
+ fullScreenPlayerView.hideWithoutPlayer();
return;
}
Activity currentActivity = themedReactContext.getCurrentActivity();
- if (currentActivity == null) return;
+ if (currentActivity == null)
+ return;
View decorView = currentActivity.getWindow().getDecorView();
ViewGroup rootView = decorView.findViewById(android.R.id.content);
@@ -2465,7 +2540,7 @@ public class ReactExoplayerView extends FrameLayout implements
LayoutParams.MATCH_PARENT);
if (isInPictureInPicture) {
- ViewGroup parent = (ViewGroup)exoPlayerView.getParent();
+ ViewGroup parent = (ViewGroup) exoPlayerView.getParent();
if (parent != null) {
parent.removeView(exoPlayerView);
}
@@ -2491,10 +2566,12 @@ public class ReactExoplayerView extends FrameLayout implements
public void enterPictureInPictureMode() {
PictureInPictureParams _pipParams = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- ArrayList<RemoteAction> actions = PictureInPictureUtil.getPictureInPictureActions(themedReactContext, isPaused, pictureInPictureReceiver);
+ ArrayList<RemoteAction> actions = PictureInPictureUtil.getPictureInPictureActions(themedReactContext,
+ isPaused, pictureInPictureReceiver);
pictureInPictureParamsBuilder.setActions(actions);
if (player.getPlaybackState() == Player.STATE_READY) {
- pictureInPictureParamsBuilder.setAspectRatio(PictureInPictureUtil.calcPictureInPictureAspectRatio(player));
+ pictureInPictureParamsBuilder
+ .setAspectRatio(PictureInPictureUtil.calcPictureInPictureAspectRatio(player));
}
_pipParams = pictureInPictureParamsBuilder.build();
}
@@ -2503,13 +2580,15 @@ public class ReactExoplayerView extends FrameLayout implements
public void exitPictureInPictureMode() {
Activity currentActivity = themedReactContext.getCurrentActivity();
- if (currentActivity == null) return;
+ if (currentActivity == null)
+ return;
View decorView = currentActivity.getWindow().getDecorView();
ViewGroup rootView = decorView.findViewById(android.R.id.content);
if (!rootViewChildrenOriginalVisibility.isEmpty()) {
- if (exoPlayerView.getParent().equals(rootView)) rootView.removeView(exoPlayerView);
+ if (exoPlayerView.getParent().equals(rootView))
+ rootView.removeView(exoPlayerView);
for (int i = 0; i < rootView.getChildCount(); i++) {
rootView.getChildAt(i).setVisibility(rootViewChildrenOriginalVisibility.get(i));
}
@@ -2607,7 +2686,7 @@ public class ReactExoplayerView extends FrameLayout implements
if (playbackServiceConnection == null && showNotificationControls) {
setupPlaybackService();
- } else if(!showNotificationControls && playbackServiceConnection != null) {
+ } else if (!showNotificationControls && playbackServiceConnection != null) {
cleanupPlaybackService();
}
}
@@ -2636,12 +2715,13 @@ public class ReactExoplayerView extends FrameLayout implements
}
if (isFullscreen) {
- fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, this, null, new OnBackPressedCallback(true) {
- @Override
- public void handleOnBackPressed() {
- setFullscreen(false);
- }
- }, controlsConfig);
+ fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, this, null,
+ new OnBackPressedCallback(true) {
+ @Override
+ public void handleOnBackPressed() {
+ setFullscreen(false);
+ }
+ }, controlsConfig);
eventEmitter.onVideoFullscreenPlayerWillPresent.invoke();
if (fullScreenPlayerView != null) {
fullScreenPlayerView.show();
@@ -2678,7 +2758,8 @@ public class ReactExoplayerView extends FrameLayout implements
}
@Override
- public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, @NonNull Exception e) {
+ public void onDrmSessionManagerError(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId,
+ @NonNull Exception e) {
DebugLog.d("DRM Info", "onDrmSessionManagerError");
eventEmitter.onVideoError.invoke("onDrmSessionManagerError", e, "3002");
}
@@ -2696,7 +2777,7 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Handling controls prop
*
- * @param controls Controls prop, if true enable controls, if false disable them
+ * @param controls Controls prop, if true enable controls, if false disable them
*/
public void setControls(boolean controls) {
this.controls = controls;
@@ -2705,7 +2786,7 @@ public class ReactExoplayerView extends FrameLayout implements
// Additional configuration for proper touch handling
if (controls) {
exoPlayerView.setControllerAutoShow(true);
- exoPlayerView.setControllerHideOnTouch(true); // Show controls on touch, don't hide
+ exoPlayerView.setControllerHideOnTouch(true); // Show controls on touch, don't hide
exoPlayerView.setControllerShowTimeoutMs(5000);
}
}
@@ -2738,8 +2819,7 @@ public class ReactExoplayerView extends FrameLayout implements
Map<String, String> errMap = Map.of(
"message", error.getMessage(),
"code", String.valueOf(error.getErrorCode()),
- "type", String.valueOf(error.getErrorType())
- );
+ "type", String.valueOf(error.getErrorType()));
eventEmitter.onReceiveAdEvent.invoke("ERROR", errMap);
}