diff --git a/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt new file mode 100644 index 00000000..297a1bf6 --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/api/BufferConfig.kt @@ -0,0 +1,66 @@ +package com.brentvatne.common.api + +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetDouble +import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt +import com.facebook.react.bridge.ReadableMap + +/** + * Class representing bufferConfig for host. + * Only generic code here, no reference to the player. + * By default, if application don't provide input field, -1 is set instead + */ +class BufferConfig { + var cacheSize = BufferConfigPropUnsetInt + var minBufferMs = BufferConfigPropUnsetInt + var maxBufferMs = BufferConfigPropUnsetInt + var bufferForPlaybackMs = BufferConfigPropUnsetInt + var bufferForPlaybackAfterRebufferMs = BufferConfigPropUnsetInt + var backBufferDurationMs = BufferConfigPropUnsetInt + var maxHeapAllocationPercent = BufferConfigPropUnsetDouble + var minBackBufferMemoryReservePercent = BufferConfigPropUnsetDouble + var minBufferMemoryReservePercent = BufferConfigPropUnsetDouble + + companion object { + val BufferConfigPropUnsetInt = -1 + val BufferConfigPropUnsetDouble = -1.0 + + private val PROP_BUFFER_CONFIG_CACHE_SIZE = "cacheSizeMB" + private val PROP_BUFFER_CONFIG_MIN_BUFFER_MS = "minBufferMs" + private val PROP_BUFFER_CONFIG_MAX_BUFFER_MS = "maxBufferMs" + private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS = "bufferForPlaybackMs" + private val PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = "bufferForPlaybackAfterRebufferMs" + private val PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT = "maxHeapAllocationPercent" + private val PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT = "minBackBufferMemoryReservePercent" + private val PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT = "minBufferMemoryReservePercent" + private val PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS = "backBufferDurationMs" + + @JvmStatic + fun parse(src: ReadableMap?): BufferConfig { + val bufferConfig = BufferConfig() + + if (src != null) { + bufferConfig.cacheSize = safeGetInt(src, PROP_BUFFER_CONFIG_CACHE_SIZE, BufferConfigPropUnsetInt) + bufferConfig.minBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MIN_BUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.maxBufferMs = safeGetInt(src, PROP_BUFFER_CONFIG_MAX_BUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.bufferForPlaybackMs = safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_MS, BufferConfigPropUnsetInt) + bufferConfig.bufferForPlaybackAfterRebufferMs = + safeGetInt(src, PROP_BUFFER_CONFIG_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, BufferConfigPropUnsetInt) + bufferConfig.maxHeapAllocationPercent = + safeGetDouble(src, PROP_BUFFER_CONFIG_MAX_HEAP_ALLOCATION_PERCENT, BufferConfigPropUnsetDouble) + bufferConfig.minBackBufferMemoryReservePercent = safeGetDouble( + src, + PROP_BUFFER_CONFIG_MIN_BACK_BUFFER_MEMORY_RESERVE_PERCENT, + BufferConfigPropUnsetDouble + ) + bufferConfig.minBufferMemoryReservePercent = + safeGetDouble( + src, + PROP_BUFFER_CONFIG_MIN_BUFFER_MEMORY_RESERVE_PERCENT, + BufferConfigPropUnsetDouble + ) + bufferConfig.backBufferDurationMs = safeGetInt(src, PROP_BUFFER_CONFIG_BACK_BUFFER_DURATION_MS, BufferConfigPropUnsetInt) + } + return bufferConfig + } + } +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt index cec4b898..23d51f77 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerSimpleCache.kt @@ -15,7 +15,7 @@ object RNVSimpleCache { var cacheDataSourceFactory: DataSource.Factory? = null fun setSimpleCache(context: Context, cacheSize: Int, factory: HttpDataSource.Factory) { - if (cacheDataSourceFactory != null || cacheSize == 0) return + if (cacheDataSourceFactory != null || cacheSize <= 0) return simpleCache = SimpleCache( File(context.cacheDir, "RNVCache"), LeastRecentlyUsedCacheEvictor( diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index d453cb5a..546c43a7 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -96,6 +96,7 @@ import androidx.media3.extractor.metadata.id3.Id3Frame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; import androidx.media3.ui.LegacyPlayerControlView; +import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.api.TimedMetadata; @@ -185,18 +186,11 @@ public class ReactExoplayerView extends FrameLayout implements private AudioOutput audioOutput = AudioOutput.SPEAKER; private float audioVolume = 1f; private int minLoadRetryCount = 3; + private BufferConfig bufferConfig = new BufferConfig(); private int maxBitRate = 0; private boolean hasDrmFailed = false; private boolean isUsingContentResolution = false; private boolean selectTrackWhenReady = false; - private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS; - private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS; - private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; - private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; - private int backBufferDurationMs = DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS; - private double maxHeapAllocationPercent = ReactExoplayerView.DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; - private double minBackBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE; - private double minBufferMemoryReservePercent = ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; private Handler mainHandler; private Runnable mainRunnable; private DataSource.Factory cacheDataSourceFactory; @@ -497,19 +491,32 @@ public class ReactExoplayerView extends FrameLayout implements private class RNVLoadControl extends DefaultLoadControl { private final int availableHeapInBytes; private final Runtime runtime; - public RNVLoadControl(DefaultAllocator allocator, int minBufferMs, int maxBufferMs, int bufferForPlaybackMs, int bufferForPlaybackAfterRebufferMs, int targetBufferBytes, boolean prioritizeTimeOverSizeThresholds, int backBufferDurationMs, boolean retainBackBufferFromKeyframe) { + public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) { super(allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - targetBufferBytes, - prioritizeTimeOverSizeThresholds, - backBufferDurationMs, - retainBackBufferFromKeyframe); + config.getMinBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getMinBufferMs() + : DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + config.getMaxBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getMaxBufferMs() + : DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + config.getBufferForPlaybackMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBufferForPlaybackMs() + : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS , + config.getBufferForPlaybackAfterRebufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBufferForPlaybackAfterRebufferMs() + : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, + -1, + true, + config.getBackBufferDurationMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt() + ? config.getBackBufferDurationMs() + : DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS, + DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME); runtime = Runtime.getRuntime(); ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE); - availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeapAllocationPercent * 1024 * 1024); + double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble() + ? bufferConfig.getMaxHeapAllocationPercent() + : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT; + availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024); } @Override @@ -524,6 +531,9 @@ public class ReactExoplayerView extends FrameLayout implements } long usedMemory = runtime.totalMemory() - runtime.freeMemory(); long freeMemory = runtime.maxMemory() - usedMemory; + double minBufferMemoryReservePercent = bufferConfig.getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble() + ? bufferConfig.getMinBufferMemoryReservePercent() + : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE; long reserveMemory = (long)minBufferMemoryReservePercent * runtime.maxMemory(); long bufferedMs = bufferedDurationUs / (long)1000; if (reserveMemory > freeMemory && bufferedMs > 2000) { @@ -604,14 +614,7 @@ public class ReactExoplayerView extends FrameLayout implements DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE); RNVLoadControl loadControl = new RNVLoadControl( allocator, - minBufferMs, - maxBufferMs, - bufferForPlaybackMs, - bufferForPlaybackAfterRebufferMs, - -1, - true, - backBufferDurationMs, - DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME + bufferConfig ); DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) @@ -2034,26 +2037,18 @@ public class ReactExoplayerView extends FrameLayout implements exoPlayerView.setHideShutterView(hideShutterView); } - public void setBufferConfig(int newMinBufferMs, int newMaxBufferMs, int newBufferForPlaybackMs, int newBufferForPlaybackAfterRebufferMs, double newMaxHeapAllocationPercent, double newMinBackBufferMemoryReservePercent, double newMinBufferMemoryReservePercent, int newBackBufferDurationMs, int cacheSize) { - minBufferMs = newMinBufferMs; - maxBufferMs = newMaxBufferMs; - bufferForPlaybackMs = newBufferForPlaybackMs; - bufferForPlaybackAfterRebufferMs = newBufferForPlaybackAfterRebufferMs; - maxHeapAllocationPercent = newMaxHeapAllocationPercent; - minBackBufferMemoryReservePercent = newMinBackBufferMemoryReservePercent; - minBufferMemoryReservePercent = newMinBufferMemoryReservePercent; - if (cacheSize > 0) { + public void setBufferConfig(BufferConfig config) { + bufferConfig = config; + if (bufferConfig.getCacheSize() > 0) { RNVSimpleCache.INSTANCE.setSimpleCache( this.getContext(), - cacheSize, + bufferConfig.getCacheSize(), buildHttpDataSourceFactory(false) ); cacheDataSourceFactory = RNVSimpleCache.INSTANCE.getCacheDataSourceFactory(); } else { cacheDataSourceFactory = null; } - - backBufferDurationMs = newBackBufferDurationMs; releasePlayer(); initializePlayer(); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 1e9ffd60..1f50faa1 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -11,6 +11,7 @@ import androidx.media3.common.util.Util; import androidx.media3.datasource.RawResourceDataSource; import androidx.media3.exoplayer.DefaultLoadControl; +import com.brentvatne.common.api.BufferConfig; import com.brentvatne.common.api.ResizeMode; import com.brentvatne.common.api.SubtitleStyle; import com.brentvatne.common.react.VideoEventEmitter; @@ -59,15 +60,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager